mirror of
https://github.com/Alfresco/SearchServices.git
synced 2026-09-16 18:12:56 +00:00
Merge branch 'master' of https://git.alfresco.com/search_discovery/insightengine into feature/Search-1824
This commit is contained in:
+1
-1
@@ -13,7 +13,7 @@
|
||||
<properties>
|
||||
<tas.rest.api.version>6.0.1.2</tas.rest.api.version>
|
||||
<tas.cmis.api.version>6.0.0.4</tas.cmis.api.version>
|
||||
<tas.utility.version>3.0.13</tas.utility.version>
|
||||
<tas.utility.version>3.0.14</tas.utility.version>
|
||||
<rm.version>3.2.0</rm.version>
|
||||
<suiteXmlFile>src/test/resources/SearchSuite.xml</suiteXmlFile>
|
||||
<test.exclude></test.exclude>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
<solr.base.version>6.6.5</solr.base.version>
|
||||
<solr.version>${solr.base.version}-patched</solr.version>
|
||||
<solr.version>${solr.base.version}-patched.1</solr.version>
|
||||
<!-- The location to download the solr zip file from. -->
|
||||
<!-- <solr.zip>https://archive.apache.org/dist/lucene/solr/${solr.version}/solr-${solr.version}.zip</solr.zip> -->
|
||||
<!-- Solr startup scripts do not work with any Java version higher than 9 so the scripts have been patched -->
|
||||
@@ -54,6 +54,19 @@
|
||||
<showDeprecation>true</showDeprecation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>2.22.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
|
||||
@@ -179,6 +179,45 @@
|
||||
<directory>src/main/resources/solr/instance/templates/rerank/conf</directory>
|
||||
<excludes>
|
||||
<exclude>solrconfig.xml</exclude>
|
||||
<exclude>solrcore.properties</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-production-solr-configuration-for-master</id>
|
||||
<phase>generate-test-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.testOutputDirectory}/test-files/master/conf</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources/solr/instance/templates/rerank/conf</directory>
|
||||
<excludes>
|
||||
<exclude>solrconfig.xml</exclude>
|
||||
<exclude>solrcore.properties</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-production-solr-configuration-for-slave</id>
|
||||
<phase>generate-test-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.testOutputDirectory}/test-files/slave/conf</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources/solr/instance/templates/rerank/conf</directory>
|
||||
<excludes>
|
||||
<exclude>solrconfig.xml</exclude>
|
||||
<exclude>solrcore.properties</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
@@ -292,7 +331,7 @@
|
||||
</goals>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>**/AnnotationWriter.*</exclude>
|
||||
<exclude>**/libs/*</exclude>
|
||||
</excludes>
|
||||
<!-- Sets the path to the file which contains the execution data. -->
|
||||
<dataFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</dataFile>
|
||||
|
||||
+773
-719
File diff suppressed because it is too large
Load Diff
-484
@@ -1,484 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
*
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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.solr;
|
||||
|
||||
import org.alfresco.httpclient.AuthenticationException;
|
||||
import org.alfresco.service.cmr.repository.datatype.Duration;
|
||||
import org.alfresco.solr.client.Node;
|
||||
import org.alfresco.solr.tracker.*;
|
||||
import org.alfresco.util.CachingDateFormat;
|
||||
import org.apache.commons.codec.EncoderException;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.common.util.SimpleOrderedMap;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Methods taken from AlfrescoCoreAdminHandler that deal with building reports
|
||||
*/
|
||||
public class HandlerReportBuilder {
|
||||
|
||||
/**
|
||||
* Builds AclReport
|
||||
* @param tracker
|
||||
* @param aclid
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static NamedList<Object> buildAclReport(AclTracker tracker, Long aclid) throws IOException, JSONException
|
||||
{
|
||||
AclReport aclReport = tracker.checkAcl(aclid);
|
||||
|
||||
NamedList<Object> nr = new SimpleOrderedMap<Object>();
|
||||
nr.add("Acl Id", aclReport.getAclId());
|
||||
nr.add("Acl doc in index", aclReport.getIndexAclDoc());
|
||||
if (aclReport.getIndexAclDoc() != null)
|
||||
{
|
||||
nr.add("Acl tx in Index", aclReport.getIndexAclTx());
|
||||
}
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds TxReport
|
||||
* @param trackerRegistry
|
||||
* @param srv
|
||||
* @param coreName
|
||||
* @param tracker
|
||||
* @param txid
|
||||
* @return
|
||||
* @throws AuthenticationException
|
||||
* @throws IOException
|
||||
* @throws JSONException
|
||||
* @throws EncoderException
|
||||
*/
|
||||
public static NamedList<Object> buildTxReport(TrackerRegistry trackerRegistry, InformationServer srv, String coreName, MetadataTracker tracker, Long txid)
|
||||
throws AuthenticationException, IOException, JSONException, EncoderException
|
||||
{
|
||||
NamedList<Object> nr = new SimpleOrderedMap<Object>();
|
||||
nr.add("TXID", txid);
|
||||
nr.add("transaction", buildTrackerReport(trackerRegistry, srv, coreName, txid, txid, 0l, 0l, null, null));
|
||||
NamedList<Object> nodes = new SimpleOrderedMap<Object>();
|
||||
// add node reports ....
|
||||
List<Node> dbNodes = tracker.getFullNodesForDbTransaction(txid);
|
||||
for (Node node : dbNodes)
|
||||
{
|
||||
nodes.add("DBID " + node.getId(), buildNodeReport(tracker, node));
|
||||
}
|
||||
|
||||
nr.add("txDbNodeCount", dbNodes.size());
|
||||
nr.add("nodes", nodes);
|
||||
return nr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds AclTxReport
|
||||
* @param trackerRegistry
|
||||
* @param srv
|
||||
* @param coreName
|
||||
* @param tracker
|
||||
* @param acltxid
|
||||
* @return
|
||||
* @throws AuthenticationException
|
||||
* @throws IOException
|
||||
* @throws JSONException
|
||||
* @throws EncoderException
|
||||
*/
|
||||
public static NamedList<Object> buildAclTxReport(TrackerRegistry trackerRegistry, InformationServer srv, String coreName, AclTracker tracker, Long acltxid)
|
||||
throws AuthenticationException, IOException, JSONException, EncoderException
|
||||
{
|
||||
NamedList<Object> nr = new SimpleOrderedMap<Object>();
|
||||
nr.add("TXID", acltxid);
|
||||
nr.add("transaction", buildTrackerReport(trackerRegistry, srv, coreName, 0l, 0l, acltxid, acltxid, null, null));
|
||||
NamedList<Object> nodes = new SimpleOrderedMap<Object>();
|
||||
// add node reports ....
|
||||
List<Long> dbAclIds = tracker.getAclsForDbAclTransaction(acltxid);
|
||||
for (Long aclid : dbAclIds)
|
||||
{
|
||||
nodes.add("ACLID " + aclid, buildAclReport(tracker, aclid));
|
||||
}
|
||||
nr.add("aclTxDbAclCount", dbAclIds.size());
|
||||
nr.add("nodes", nodes);
|
||||
return nr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds Node report
|
||||
* @param tracker
|
||||
* @param node
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static NamedList<Object> buildNodeReport(MetadataTracker tracker, Node node) throws IOException, JSONException
|
||||
{
|
||||
NodeReport nodeReport = tracker.checkNode(node);
|
||||
|
||||
NamedList<Object> nr = new SimpleOrderedMap<Object>();
|
||||
nr.add("Node DBID", nodeReport.getDbid());
|
||||
nr.add("DB TX", nodeReport.getDbTx());
|
||||
nr.add("DB TX status", nodeReport.getDbNodeStatus().toString());
|
||||
if (nodeReport.getIndexLeafDoc() != null)
|
||||
{
|
||||
nr.add("Leaf tx in Index", nodeReport.getIndexLeafTx());
|
||||
}
|
||||
if (nodeReport.getIndexAuxDoc() != null)
|
||||
{
|
||||
nr.add("Aux tx in Index", nodeReport.getIndexAuxTx());
|
||||
}
|
||||
nr.add("Indexed Node Doc Count", nodeReport.getIndexedNodeDocCount());
|
||||
return nr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds Node Report
|
||||
* @param tracker
|
||||
* @param dbid
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static NamedList<Object> buildNodeReport(MetadataTracker tracker, Long dbid) throws IOException, JSONException
|
||||
{
|
||||
NodeReport nodeReport = tracker.checkNode(dbid);
|
||||
|
||||
NamedList<Object> nr = new SimpleOrderedMap<Object>();
|
||||
nr.add("Node DBID", nodeReport.getDbid());
|
||||
nr.add("DB TX", nodeReport.getDbTx());
|
||||
nr.add("DB TX status", nodeReport.getDbNodeStatus().toString());
|
||||
if (nodeReport.getIndexLeafDoc() != null)
|
||||
{
|
||||
nr.add("Leaf tx in Index", nodeReport.getIndexLeafTx());
|
||||
}
|
||||
if (nodeReport.getIndexAuxDoc() != null)
|
||||
{
|
||||
nr.add("Aux tx in Index", nodeReport.getIndexAuxTx());
|
||||
}
|
||||
nr.add("Indexed Node Doc Count", nodeReport.getIndexedNodeDocCount());
|
||||
return nr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds Tracker report
|
||||
* @param trackerRegistry
|
||||
* @param srv
|
||||
* @param coreName
|
||||
* @param fromTx
|
||||
* @param toTx
|
||||
* @param fromAclTx
|
||||
* @param toAclTx
|
||||
* @param fromTime
|
||||
* @param toTime
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws JSONException
|
||||
* @throws AuthenticationException
|
||||
* @throws EncoderException
|
||||
*/
|
||||
public static NamedList<Object> buildTrackerReport(TrackerRegistry trackerRegistry, InformationServer srv, String coreName, Long fromTx, Long toTx, Long fromAclTx, Long toAclTx,
|
||||
Long fromTime, Long toTime) throws IOException, JSONException, AuthenticationException, EncoderException
|
||||
{
|
||||
// ACL
|
||||
AclTracker aclTracker = trackerRegistry.getTrackerForCore(coreName, AclTracker.class);
|
||||
IndexHealthReport aclReport = aclTracker.checkIndex(toTx, toAclTx, fromTime, toTime);
|
||||
NamedList<Object> ihr = new SimpleOrderedMap<Object>();
|
||||
ihr.add("Alfresco version", aclTracker.getAlfrescoVersion());
|
||||
ihr.add("DB acl transaction count", aclReport.getDbAclTransactionCount());
|
||||
ihr.add("Count of duplicated acl transactions in the index", aclReport.getDuplicatedAclTxInIndex()
|
||||
.cardinality());
|
||||
if (aclReport.getDuplicatedAclTxInIndex().cardinality() > 0)
|
||||
{
|
||||
ihr.add("First duplicate acl tx", aclReport.getDuplicatedAclTxInIndex().nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Count of acl transactions in the index but not the DB", aclReport.getAclTxInIndexButNotInDb()
|
||||
.cardinality());
|
||||
if (aclReport.getAclTxInIndexButNotInDb().cardinality() > 0)
|
||||
{
|
||||
ihr.add("First acl transaction in the index but not the DB", aclReport.getAclTxInIndexButNotInDb()
|
||||
.nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Count of missing acl transactions from the Index", aclReport.getMissingAclTxFromIndex()
|
||||
.cardinality());
|
||||
if (aclReport.getMissingAclTxFromIndex().cardinality() > 0)
|
||||
{
|
||||
ihr.add("First acl transaction missing from the Index", aclReport.getMissingAclTxFromIndex()
|
||||
.nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Index acl transaction count", aclReport.getAclTransactionDocsInIndex());
|
||||
ihr.add("Index unique acl transaction count", aclReport.getAclTransactionDocsInIndex());
|
||||
TrackerState aclState = aclTracker.getTrackerState();
|
||||
ihr.add("Last indexed change set commit time", aclState.getLastIndexedChangeSetCommitTime());
|
||||
Date lastChangeSetDate = new Date(aclState.getLastIndexedChangeSetCommitTime());
|
||||
ihr.add("Last indexed change set commit date", CachingDateFormat.getDateFormat().format(lastChangeSetDate));
|
||||
ihr.add("Last changeset id before holes", aclState.getLastIndexedChangeSetIdBeforeHoles());
|
||||
|
||||
// Metadata
|
||||
MetadataTracker metadataTracker = trackerRegistry.getTrackerForCore(coreName, MetadataTracker.class);
|
||||
IndexHealthReport metaReport = metadataTracker.checkIndex(toTx, toAclTx, fromTime, toTime);
|
||||
ihr.add("DB transaction count", metaReport.getDbTransactionCount());
|
||||
ihr.add("Count of duplicated transactions in the index", metaReport.getDuplicatedTxInIndex()
|
||||
.cardinality());
|
||||
if (metaReport.getDuplicatedTxInIndex().cardinality() > 0)
|
||||
{
|
||||
ihr.add("First duplicate", metaReport.getDuplicatedTxInIndex().nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Count of transactions in the index but not the DB", metaReport.getTxInIndexButNotInDb()
|
||||
.cardinality());
|
||||
if (metaReport.getTxInIndexButNotInDb().cardinality() > 0)
|
||||
{
|
||||
ihr.add("First transaction in the index but not the DB", metaReport.getTxInIndexButNotInDb()
|
||||
.nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Count of missing transactions from the Index", metaReport.getMissingTxFromIndex().cardinality());
|
||||
if (metaReport.getMissingTxFromIndex().cardinality() > 0)
|
||||
{
|
||||
ihr.add("First transaction missing from the Index", metaReport.getMissingTxFromIndex()
|
||||
.nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Index transaction count", metaReport.getTransactionDocsInIndex());
|
||||
ihr.add("Index unique transaction count", metaReport.getTransactionDocsInIndex());
|
||||
ihr.add("Index node count", metaReport.getLeafDocCountInIndex());
|
||||
ihr.add("Count of duplicate nodes in the index", metaReport.getDuplicatedLeafInIndex().cardinality());
|
||||
if (metaReport.getDuplicatedLeafInIndex().cardinality() > 0)
|
||||
{
|
||||
ihr.add("First duplicate node id in the index", metaReport.getDuplicatedLeafInIndex().nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Index error count", metaReport.getErrorDocCountInIndex());
|
||||
ihr.add("Count of duplicate error docs in the index", metaReport.getDuplicatedErrorInIndex()
|
||||
.cardinality());
|
||||
if (metaReport.getDuplicatedErrorInIndex().cardinality() > 0)
|
||||
{
|
||||
ihr.add("First duplicate error in the index", SolrInformationServer.PREFIX_ERROR
|
||||
+ metaReport.getDuplicatedErrorInIndex().nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Index unindexed count", metaReport.getUnindexedDocCountInIndex());
|
||||
ihr.add("Count of duplicate unindexed docs in the index", metaReport.getDuplicatedUnindexedInIndex()
|
||||
.cardinality());
|
||||
if (metaReport.getDuplicatedUnindexedInIndex().cardinality() > 0)
|
||||
{
|
||||
ihr.add("First duplicate unindexed in the index",
|
||||
metaReport.getDuplicatedUnindexedInIndex().nextSetBit(0L));
|
||||
}
|
||||
TrackerState metaState = metadataTracker.getTrackerState();
|
||||
ihr.add("Last indexed transaction commit time", metaState.getLastIndexedTxCommitTime());
|
||||
Date lastTxDate = new Date(metaState.getLastIndexedTxCommitTime());
|
||||
ihr.add("Last indexed transaction commit date", CachingDateFormat.getDateFormat().format(lastTxDate));
|
||||
ihr.add("Last TX id before holes", metaState.getLastIndexedTxIdBeforeHoles());
|
||||
|
||||
srv.addFTSStatusCounts(ihr);
|
||||
|
||||
return ihr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a core summary
|
||||
* @param cname
|
||||
* @param detail
|
||||
* @param hist
|
||||
* @param values
|
||||
* @param srv
|
||||
* @param report
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void addCoreSummary(TrackerRegistry trackerRegistry, String cname, boolean detail, boolean hist, boolean values,
|
||||
InformationServer srv, NamedList<Object> report) throws IOException
|
||||
{
|
||||
NamedList<Object> coreSummary = new SimpleOrderedMap<Object>();
|
||||
coreSummary.addAll((SimpleOrderedMap<Object>) srv.getCoreStats());
|
||||
|
||||
MetadataTracker metaTrkr = trackerRegistry.getTrackerForCore(cname, MetadataTracker.class);
|
||||
TrackerState metadataTrkrState = metaTrkr.getTrackerState();
|
||||
long lastIndexTxCommitTime = metadataTrkrState.getLastIndexedTxCommitTime();
|
||||
|
||||
long lastIndexedTxId = metadataTrkrState.getLastIndexedTxId();
|
||||
long lastTxCommitTimeOnServer = metadataTrkrState.getLastTxCommitTimeOnServer();
|
||||
long lastTxIdOnServer = metadataTrkrState.getLastTxIdOnServer();
|
||||
Date lastIndexTxCommitDate = new Date(lastIndexTxCommitTime);
|
||||
Date lastTxOnServerDate = new Date(lastTxCommitTimeOnServer);
|
||||
long transactionsToDo = lastTxIdOnServer - lastIndexedTxId;
|
||||
if (transactionsToDo < 0)
|
||||
{
|
||||
transactionsToDo = 0;
|
||||
}
|
||||
|
||||
AclTracker aclTrkr = trackerRegistry.getTrackerForCore(cname, AclTracker.class);
|
||||
TrackerState aclTrkrState = aclTrkr.getTrackerState();
|
||||
long lastIndexChangeSetCommitTime = aclTrkrState.getLastIndexedChangeSetCommitTime();
|
||||
long lastIndexedChangeSetId = aclTrkrState.getLastIndexedChangeSetId();
|
||||
long lastChangeSetCommitTimeOnServer = aclTrkrState.getLastChangeSetCommitTimeOnServer();
|
||||
long lastChangeSetIdOnServer = aclTrkrState.getLastChangeSetIdOnServer();
|
||||
Date lastIndexChangeSetCommitDate = new Date(lastIndexChangeSetCommitTime);
|
||||
Date lastChangeSetOnServerDate = new Date(lastChangeSetCommitTimeOnServer);
|
||||
long changeSetsToDo = lastChangeSetIdOnServer - lastIndexedChangeSetId;
|
||||
if (changeSetsToDo < 0)
|
||||
{
|
||||
changeSetsToDo = 0;
|
||||
}
|
||||
|
||||
long nodesToDo = 0;
|
||||
long remainingTxTimeMillis = 0;
|
||||
if (transactionsToDo > 0)
|
||||
{
|
||||
// We now use the elapsed time as seen by the single thread farming out metadata indexing
|
||||
double meanDocsPerTx = srv.getTrackerStats().getMeanDocsPerTx();
|
||||
double meanNodeElaspedIndexTime = srv.getTrackerStats().getMeanNodeElapsedIndexTime();
|
||||
nodesToDo = (long)(transactionsToDo * meanDocsPerTx);
|
||||
remainingTxTimeMillis = (long) (nodesToDo * meanNodeElaspedIndexTime);
|
||||
}
|
||||
Date now = new Date();
|
||||
Date end = new Date(now.getTime() + remainingTxTimeMillis);
|
||||
Duration remainingTx = new Duration(now, end);
|
||||
|
||||
long remainingChangeSetTimeMillis = 0;
|
||||
if (changeSetsToDo > 0)
|
||||
{
|
||||
// We now use the elapsed time as seen by the single thread farming out alc indexing
|
||||
double meanAclsPerChangeSet = srv.getTrackerStats().getMeanAclsPerChangeSet();
|
||||
double meanAclElapsedIndexTime = srv.getTrackerStats().getMeanAclElapsedIndexTime();
|
||||
remainingChangeSetTimeMillis = (long) (changeSetsToDo * meanAclsPerChangeSet * meanAclElapsedIndexTime);
|
||||
}
|
||||
now = new Date();
|
||||
end = new Date(now.getTime() + remainingChangeSetTimeMillis);
|
||||
Duration remainingChangeSet = new Duration(now, end);
|
||||
|
||||
NamedList<Object> ftsSummary = new SimpleOrderedMap<Object>();
|
||||
long remainingContentTimeMillis = 0;
|
||||
srv.addFTSStatusCounts(ftsSummary);
|
||||
long cleanCount = ((Long)ftsSummary.get("Node count with FTSStatus Clean")).longValue();
|
||||
long dirtyCount = ((Long)ftsSummary.get("Node count with FTSStatus Dirty")).longValue();
|
||||
long newCount = ((Long)ftsSummary.get("Node count with FTSStatus New")).longValue();
|
||||
long nodesInIndex = ((Long)coreSummary.get("Alfresco Nodes in Index"));
|
||||
long contentYetToSee = nodesInIndex > 0 ? nodesToDo * (cleanCount + dirtyCount + newCount)/nodesInIndex : 0;;
|
||||
if (dirtyCount + newCount + contentYetToSee > 0)
|
||||
{
|
||||
// We now use the elapsed time as seen by the single thread farming out alc indexing
|
||||
double meanContentElapsedIndexTime = srv.getTrackerStats().getMeanContentElapsedIndexTime();
|
||||
remainingContentTimeMillis = (long) ((dirtyCount + newCount + contentYetToSee) * meanContentElapsedIndexTime);
|
||||
}
|
||||
now = new Date();
|
||||
end = new Date(now.getTime() + remainingContentTimeMillis);
|
||||
Duration remainingContent = new Duration(now, end);
|
||||
coreSummary.add("FTS",ftsSummary);
|
||||
|
||||
Duration txLag = new Duration(lastIndexTxCommitDate, lastTxOnServerDate);
|
||||
if (lastIndexTxCommitDate.compareTo(lastTxOnServerDate) > 0)
|
||||
{
|
||||
txLag = new Duration();
|
||||
}
|
||||
long txLagSeconds = (lastTxCommitTimeOnServer - lastIndexTxCommitTime) / 1000;
|
||||
if (txLagSeconds < 0)
|
||||
{
|
||||
txLagSeconds = 0;
|
||||
}
|
||||
|
||||
Duration changeSetLag = new Duration(lastIndexChangeSetCommitDate, lastChangeSetOnServerDate);
|
||||
if (lastIndexChangeSetCommitDate.compareTo(lastChangeSetOnServerDate) > 0)
|
||||
{
|
||||
changeSetLag = new Duration();
|
||||
}
|
||||
long changeSetLagSeconds = (lastChangeSetCommitTimeOnServer - lastIndexChangeSetCommitTime) / 1000;
|
||||
if (txLagSeconds < 0)
|
||||
{
|
||||
txLagSeconds = 0;
|
||||
}
|
||||
|
||||
ContentTracker contentTrkr = trackerRegistry.getTrackerForCore(cname, ContentTracker.class);
|
||||
TrackerState contentTrkrState = contentTrkr.getTrackerState();
|
||||
// Leave ModelTracker out of this check, because it is common
|
||||
boolean aTrackerIsRunning = aclTrkrState.isRunning() || metadataTrkrState.isRunning()
|
||||
|| contentTrkrState.isRunning();
|
||||
coreSummary.add("Active", aTrackerIsRunning);
|
||||
|
||||
ModelTracker modelTrkr = trackerRegistry.getModelTracker();
|
||||
TrackerState modelTrkrState = modelTrkr.getTrackerState();
|
||||
coreSummary.add("ModelTracker Active", modelTrkrState.isRunning());
|
||||
coreSummary.add("ContentTracker Active", contentTrkrState.isRunning());
|
||||
coreSummary.add("MetadataTracker Active", metadataTrkrState.isRunning());
|
||||
coreSummary.add("AclTracker Active", aclTrkrState.isRunning());
|
||||
|
||||
// TX
|
||||
|
||||
coreSummary.add("Last Index TX Commit Time", lastIndexTxCommitTime);
|
||||
coreSummary.add("Last Index TX Commit Date", lastIndexTxCommitDate);
|
||||
coreSummary.add("TX Lag", txLagSeconds + " s");
|
||||
coreSummary.add("TX Duration", txLag.toString());
|
||||
coreSummary.add("Timestamp for last TX on server", lastTxCommitTimeOnServer);
|
||||
coreSummary.add("Date for last TX on server", lastTxOnServerDate);
|
||||
coreSummary.add("Id for last TX on server", lastTxIdOnServer);
|
||||
coreSummary.add("Id for last TX in index", lastIndexedTxId);
|
||||
coreSummary.add("Approx transactions remaining", transactionsToDo);
|
||||
coreSummary.add("Approx transaction indexing time remaining", remainingTx.largestComponentformattedString());
|
||||
|
||||
// Change set
|
||||
|
||||
coreSummary.add("Last Index Change Set Commit Time", lastIndexChangeSetCommitTime);
|
||||
coreSummary.add("Last Index Change Set Commit Date", lastIndexChangeSetCommitDate);
|
||||
coreSummary.add("Change Set Lag", changeSetLagSeconds + " s");
|
||||
coreSummary.add("Change Set Duration", changeSetLag.toString());
|
||||
coreSummary.add("Timestamp for last Change Set on server", lastChangeSetCommitTimeOnServer);
|
||||
coreSummary.add("Date for last Change Set on server", lastChangeSetOnServerDate);
|
||||
coreSummary.add("Id for last Change Set on server", lastChangeSetIdOnServer);
|
||||
coreSummary.add("Id for last Change Set in index", lastIndexedChangeSetId);
|
||||
coreSummary.add("Approx change sets remaining", changeSetsToDo);
|
||||
coreSummary.add("Approx change set indexing time remaining",
|
||||
remainingChangeSet.largestComponentformattedString());
|
||||
|
||||
coreSummary.add("Approx content indexing time remaining",
|
||||
remainingContent.largestComponentformattedString());
|
||||
|
||||
// Stats
|
||||
|
||||
coreSummary.add("Model sync times (ms)",
|
||||
srv.getTrackerStats().getModelTimes().getNamedList(detail, hist, values));
|
||||
coreSummary.add("Acl index time (ms)",
|
||||
srv.getTrackerStats().getAclTimes().getNamedList(detail, hist, values));
|
||||
coreSummary.add("Node index time (ms)",
|
||||
srv.getTrackerStats().getNodeTimes().getNamedList(detail, hist, values));
|
||||
coreSummary.add("Docs/Tx", srv.getTrackerStats().getTxDocs().getNamedList(detail, hist, values));
|
||||
coreSummary.add("Doc Transformation time (ms)", srv.getTrackerStats().getDocTransformationTimes()
|
||||
.getNamedList(detail, hist, values));
|
||||
|
||||
// Model
|
||||
|
||||
Map<String, Set<String>> modelErrors = srv.getModelErrors();
|
||||
if (modelErrors.size() > 0)
|
||||
{
|
||||
NamedList<Object> errorList = new SimpleOrderedMap<Object>();
|
||||
for (Map.Entry<String, Set<String>> modelNameToErrors : modelErrors.entrySet())
|
||||
{
|
||||
errorList.add(modelNameToErrors.getKey(), modelNameToErrors.getValue());
|
||||
}
|
||||
coreSummary.add("Model changes are not compatible with the existing data model and have not been applied",
|
||||
errorList);
|
||||
}
|
||||
|
||||
report.add(cname, coreSummary);
|
||||
}
|
||||
}
|
||||
+564
@@ -0,0 +1,564 @@
|
||||
/*
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
*
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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.solr;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.service.cmr.repository.datatype.Duration;
|
||||
import org.alfresco.solr.client.Node;
|
||||
import org.alfresco.solr.tracker.*;
|
||||
import org.alfresco.util.CachingDateFormat;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.common.util.SimpleOrderedMap;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
/**
|
||||
* Methods taken from AlfrescoCoreAdminHandler that deal with building reports
|
||||
*/
|
||||
class HandlerReportHelper
|
||||
{
|
||||
static NamedList<Object> buildAclReport(AclTracker tracker, Long aclid) throws JSONException
|
||||
{
|
||||
AclReport aclReport = tracker.checkAcl(aclid);
|
||||
|
||||
NamedList<Object> nr = new SimpleOrderedMap<>();
|
||||
nr.add("Acl Id", aclReport.getAclId());
|
||||
nr.add("Acl doc in index", aclReport.getIndexAclDoc());
|
||||
if (aclReport.getIndexAclDoc() != null)
|
||||
{
|
||||
nr.add("Acl tx in Index", aclReport.getIndexAclTx());
|
||||
}
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
static NamedList<Object> buildTxReport(TrackerRegistry trackerRegistry, InformationServer srv, String coreName, MetadataTracker tracker, Long txid) throws JSONException
|
||||
{
|
||||
NamedList<Object> nr = new SimpleOrderedMap<>();
|
||||
nr.add("TXID", txid);
|
||||
nr.add("transaction", buildTrackerReport(trackerRegistry, srv, coreName, txid, txid, 0L, 0L, null, null));
|
||||
NamedList<Object> nodes = new SimpleOrderedMap<>();
|
||||
|
||||
// add node reports ....
|
||||
List<Node> dbNodes = tracker.getFullNodesForDbTransaction(txid);
|
||||
for (Node node : dbNodes)
|
||||
{
|
||||
nodes.add("DBID " + node.getId(), buildNodeReport(tracker, node));
|
||||
}
|
||||
|
||||
nr.add("txDbNodeCount", dbNodes.size());
|
||||
nr.add("nodes", nodes);
|
||||
return nr;
|
||||
}
|
||||
|
||||
static NamedList<Object> buildAclTxReport(TrackerRegistry trackerRegistry, InformationServer srv, String coreName, AclTracker tracker, Long acltxid) throws JSONException
|
||||
{
|
||||
try {
|
||||
NamedList<Object> nr = new SimpleOrderedMap<>();
|
||||
nr.add("TXID", acltxid);
|
||||
nr.add("transaction", buildTrackerReport(trackerRegistry, srv, coreName, 0L, 0L, acltxid, acltxid, null, null));
|
||||
NamedList<Object> nodes = new SimpleOrderedMap<>();
|
||||
|
||||
// add node reports ....
|
||||
List<Long> dbAclIds = tracker.getAclsForDbAclTransaction(acltxid);
|
||||
for (Long aclid : dbAclIds) {
|
||||
nodes.add("ACLID " + aclid, buildAclReport(tracker, aclid));
|
||||
}
|
||||
nr.add("aclTxDbAclCount", dbAclIds.size());
|
||||
nr.add("nodes", nodes);
|
||||
return nr;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("", exception);
|
||||
}
|
||||
}
|
||||
|
||||
static NamedList<Object> buildNodeReport(MetadataTracker tracker, Node node) throws JSONException
|
||||
{
|
||||
NodeReport nodeReport = tracker.checkNode(node);
|
||||
|
||||
NamedList<Object> nr = new SimpleOrderedMap<>();
|
||||
nr.add("Node DBID", nodeReport.getDbid());
|
||||
nr.add("DB TX", nodeReport.getDbTx());
|
||||
nr.add("DB TX status", nodeReport.getDbNodeStatus().toString());
|
||||
if (nodeReport.getIndexLeafDoc() != null)
|
||||
{
|
||||
nr.add("Leaf tx in Index", nodeReport.getIndexLeafTx());
|
||||
}
|
||||
if (nodeReport.getIndexAuxDoc() != null)
|
||||
{
|
||||
nr.add("Aux tx in Index", nodeReport.getIndexAuxTx());
|
||||
}
|
||||
nr.add("Indexed Node Doc Count", nodeReport.getIndexedNodeDocCount());
|
||||
return nr;
|
||||
}
|
||||
|
||||
static NamedList<Object> buildNodeReport(CoreStatePublisher publisher, Long dbid) throws JSONException
|
||||
{
|
||||
NodeReport nodeReport = publisher.checkNode(dbid);
|
||||
|
||||
NamedList<Object> payload = new SimpleOrderedMap<>();
|
||||
payload.add("Node DBID", nodeReport.getDbid());
|
||||
|
||||
if (publisher.isOnMasterOrStandalone())
|
||||
{
|
||||
ofNullable(nodeReport.getDbTx()).ifPresent(value -> payload.add("DB TX", value));
|
||||
ofNullable(nodeReport.getDbNodeStatus()).map(Object::toString).ifPresent(value -> payload.add("DB TX Status", value));
|
||||
ofNullable(nodeReport.getIndexLeafTx()).ifPresent(value -> payload.add("Leaf tx in Index", value));
|
||||
ofNullable(nodeReport.getIndexAuxDoc()).ifPresent(value -> payload.add("Aux tx in Index", value));
|
||||
}
|
||||
else
|
||||
{
|
||||
payload.add("WARNING", "This response comes from a slave core and it contains minimal information about the node. " +
|
||||
"Please consider to re-submit the same request to the corresponding Master, in order to get more information.");
|
||||
}
|
||||
|
||||
ofNullable(nodeReport.getIndexedNodeDocCount()).ifPresent(value -> payload.add("Indexed Node Doc Count", value));
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds Tracker report
|
||||
*/
|
||||
static NamedList<Object> buildTrackerReport(TrackerRegistry trackerRegistry, InformationServer srv, String coreName, Long fromTx, Long toTx, Long fromAclTx, Long toAclTx,
|
||||
Long fromTime, Long toTime) throws JSONException
|
||||
{
|
||||
try
|
||||
{
|
||||
// ACL
|
||||
AclTracker aclTracker = trackerRegistry.getTrackerForCore(coreName, AclTracker.class);
|
||||
IndexHealthReport aclReport = aclTracker.checkIndex(toTx, toAclTx, fromTime, toTime);
|
||||
NamedList<Object> ihr = new SimpleOrderedMap<>();
|
||||
ihr.add("Alfresco version", aclTracker.getAlfrescoVersion());
|
||||
ihr.add("DB acl transaction count", aclReport.getDbAclTransactionCount());
|
||||
ihr.add("Count of duplicated acl transactions in the index", aclReport.getDuplicatedAclTxInIndex()
|
||||
.cardinality());
|
||||
if (aclReport.getDuplicatedAclTxInIndex().cardinality() > 0) {
|
||||
ihr.add("First duplicate acl tx", aclReport.getDuplicatedAclTxInIndex().nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Count of acl transactions in the index but not the DB", aclReport.getAclTxInIndexButNotInDb()
|
||||
.cardinality());
|
||||
if (aclReport.getAclTxInIndexButNotInDb().cardinality() > 0) {
|
||||
ihr.add("First acl transaction in the index but not the DB", aclReport.getAclTxInIndexButNotInDb()
|
||||
.nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Count of missing acl transactions from the Index", aclReport.getMissingAclTxFromIndex()
|
||||
.cardinality());
|
||||
if (aclReport.getMissingAclTxFromIndex().cardinality() > 0) {
|
||||
ihr.add("First acl transaction missing from the Index", aclReport.getMissingAclTxFromIndex()
|
||||
.nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Index acl transaction count", aclReport.getAclTransactionDocsInIndex());
|
||||
ihr.add("Index unique acl transaction count", aclReport.getAclTransactionDocsInIndex());
|
||||
TrackerState aclState = aclTracker.getTrackerState();
|
||||
ihr.add("Last indexed change set commit time", aclState.getLastIndexedChangeSetCommitTime());
|
||||
Date lastChangeSetDate = new Date(aclState.getLastIndexedChangeSetCommitTime());
|
||||
ihr.add("Last indexed change set commit date", CachingDateFormat.getDateFormat().format(lastChangeSetDate));
|
||||
ihr.add("Last changeset id before holes", aclState.getLastIndexedChangeSetIdBeforeHoles());
|
||||
|
||||
// Metadata
|
||||
MetadataTracker metadataTracker = trackerRegistry.getTrackerForCore(coreName, MetadataTracker.class);
|
||||
IndexHealthReport metaReport = metadataTracker.checkIndex(toTx, toAclTx, fromTime, toTime);
|
||||
ihr.add("DB transaction count", metaReport.getDbTransactionCount());
|
||||
ihr.add("Count of duplicated transactions in the index", metaReport.getDuplicatedTxInIndex()
|
||||
.cardinality());
|
||||
if (metaReport.getDuplicatedTxInIndex().cardinality() > 0) {
|
||||
ihr.add("First duplicate", metaReport.getDuplicatedTxInIndex().nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Count of transactions in the index but not the DB", metaReport.getTxInIndexButNotInDb()
|
||||
.cardinality());
|
||||
if (metaReport.getTxInIndexButNotInDb().cardinality() > 0) {
|
||||
ihr.add("First transaction in the index but not the DB", metaReport.getTxInIndexButNotInDb()
|
||||
.nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Count of missing transactions from the Index", metaReport.getMissingTxFromIndex().cardinality());
|
||||
if (metaReport.getMissingTxFromIndex().cardinality() > 0) {
|
||||
ihr.add("First transaction missing from the Index", metaReport.getMissingTxFromIndex()
|
||||
.nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Index transaction count", metaReport.getTransactionDocsInIndex());
|
||||
ihr.add("Index unique transaction count", metaReport.getTransactionDocsInIndex());
|
||||
ihr.add("Index node count", metaReport.getLeafDocCountInIndex());
|
||||
ihr.add("Count of duplicate nodes in the index", metaReport.getDuplicatedLeafInIndex().cardinality());
|
||||
if (metaReport.getDuplicatedLeafInIndex().cardinality() > 0) {
|
||||
ihr.add("First duplicate node id in the index", metaReport.getDuplicatedLeafInIndex().nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Index error count", metaReport.getErrorDocCountInIndex());
|
||||
ihr.add("Count of duplicate error docs in the index", metaReport.getDuplicatedErrorInIndex()
|
||||
.cardinality());
|
||||
if (metaReport.getDuplicatedErrorInIndex().cardinality() > 0) {
|
||||
ihr.add("First duplicate error in the index", SolrInformationServer.PREFIX_ERROR
|
||||
+ metaReport.getDuplicatedErrorInIndex().nextSetBit(0L));
|
||||
}
|
||||
ihr.add("Index unindexed count", metaReport.getUnindexedDocCountInIndex());
|
||||
ihr.add("Count of duplicate unindexed docs in the index", metaReport.getDuplicatedUnindexedInIndex()
|
||||
.cardinality());
|
||||
if (metaReport.getDuplicatedUnindexedInIndex().cardinality() > 0) {
|
||||
ihr.add("First duplicate unindexed in the index",
|
||||
metaReport.getDuplicatedUnindexedInIndex().nextSetBit(0L));
|
||||
}
|
||||
TrackerState metaState = metadataTracker.getTrackerState();
|
||||
ihr.add("Last indexed transaction commit time", metaState.getLastIndexedTxCommitTime());
|
||||
Date lastTxDate = new Date(metaState.getLastIndexedTxCommitTime());
|
||||
ihr.add("Last indexed transaction commit date", CachingDateFormat.getDateFormat().format(lastTxDate));
|
||||
ihr.add("Last TX id before holes", metaState.getLastIndexedTxIdBeforeHoles());
|
||||
|
||||
srv.addFTSStatusCounts(ihr);
|
||||
|
||||
return ihr;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("", exception);
|
||||
}
|
||||
}
|
||||
|
||||
static void addSlaveCoreSummary(TrackerRegistry trackerRegistry, String cname, boolean detail, boolean hist, boolean values,
|
||||
InformationServer srv, NamedList<Object> report) throws IOException
|
||||
{
|
||||
NamedList<Object> coreSummary = new SimpleOrderedMap<>();
|
||||
coreSummary.addAll((SimpleOrderedMap<Object>) srv.getCoreStats());
|
||||
|
||||
SlaveCoreStatePublisher statePublisher = trackerRegistry.getTrackerForCore(cname, SlaveCoreStatePublisher.class);
|
||||
TrackerState trackerState = statePublisher.getTrackerState();
|
||||
long lastIndexTxCommitTime = trackerState.getLastIndexedTxCommitTime();
|
||||
|
||||
long lastIndexedTxId = trackerState.getLastIndexedTxId();
|
||||
long lastTxCommitTimeOnServer = trackerState.getLastTxCommitTimeOnServer();
|
||||
long lastTxIdOnServer = trackerState.getLastTxIdOnServer();
|
||||
|
||||
Date lastIndexTxCommitDate = new Date(lastIndexTxCommitTime);
|
||||
Date lastTxOnServerDate = new Date(lastTxCommitTimeOnServer);
|
||||
long transactionsToDo = lastTxIdOnServer - lastIndexedTxId;
|
||||
if (transactionsToDo < 0)
|
||||
{
|
||||
transactionsToDo = 0;
|
||||
}
|
||||
|
||||
long nodesToDo = 0;
|
||||
long remainingTxTimeMillis = 0;
|
||||
if (transactionsToDo > 0)
|
||||
{
|
||||
// We now use the elapsed time as seen by the single thread farming out metadata indexing
|
||||
double meanDocsPerTx = srv.getTrackerStats().getMeanDocsPerTx();
|
||||
double meanNodeElaspedIndexTime = srv.getTrackerStats().getMeanNodeElapsedIndexTime();
|
||||
nodesToDo = (long)(transactionsToDo * meanDocsPerTx);
|
||||
remainingTxTimeMillis = (long) (nodesToDo * meanNodeElaspedIndexTime);
|
||||
}
|
||||
Date now = new Date();
|
||||
Date end = new Date(now.getTime() + remainingTxTimeMillis);
|
||||
Duration remainingTx = new Duration(now, end);
|
||||
|
||||
long remainingChangeSetTimeMillis = 0;
|
||||
|
||||
now = new Date();
|
||||
end = new Date(now.getTime() + remainingChangeSetTimeMillis);
|
||||
Duration remainingChangeSet = new Duration(now, end);
|
||||
|
||||
NamedList<Object> ftsSummary = new SimpleOrderedMap<>();
|
||||
long remainingContentTimeMillis = 0;
|
||||
srv.addFTSStatusCounts(ftsSummary);
|
||||
long cleanCount =
|
||||
ofNullable(ftsSummary.get("Node count with FTSStatus Clean"))
|
||||
.map(Number.class::cast)
|
||||
.map(Number::longValue)
|
||||
.orElse(0L);
|
||||
long dirtyCount =
|
||||
ofNullable(ftsSummary.get("Node count with FTSStatus Dirty"))
|
||||
.map(Number.class::cast)
|
||||
.map(Number::longValue)
|
||||
.orElse(0L);
|
||||
long newCount =
|
||||
ofNullable(ftsSummary.get("Node count with FTSStatus New"))
|
||||
.map(Number.class::cast)
|
||||
.map(Number::longValue)
|
||||
.orElse(0L);
|
||||
|
||||
long nodesInIndex =
|
||||
ofNullable(coreSummary.get("Alfresco Nodes in Index"))
|
||||
.map(Number.class::cast)
|
||||
.map(Number::longValue)
|
||||
.orElse(0L);
|
||||
|
||||
long contentYetToSee = nodesInIndex > 0 ? nodesToDo * (cleanCount + dirtyCount + newCount)/nodesInIndex : 0;
|
||||
if (dirtyCount + newCount + contentYetToSee > 0)
|
||||
{
|
||||
// We now use the elapsed time as seen by the single thread farming out alc indexing
|
||||
double meanContentElapsedIndexTime = srv.getTrackerStats().getMeanContentElapsedIndexTime();
|
||||
remainingContentTimeMillis = (long) ((dirtyCount + newCount + contentYetToSee) * meanContentElapsedIndexTime);
|
||||
}
|
||||
now = new Date();
|
||||
end = new Date(now.getTime() + remainingContentTimeMillis);
|
||||
Duration remainingContent = new Duration(now, end);
|
||||
coreSummary.add("FTS",ftsSummary);
|
||||
|
||||
Duration txLag = new Duration(lastIndexTxCommitDate, lastTxOnServerDate);
|
||||
if (lastIndexTxCommitDate.compareTo(lastTxOnServerDate) > 0)
|
||||
{
|
||||
txLag = new Duration();
|
||||
}
|
||||
long txLagSeconds = (lastTxCommitTimeOnServer - lastIndexTxCommitTime) / 1000;
|
||||
if (txLagSeconds < 0)
|
||||
{
|
||||
txLagSeconds = 0;
|
||||
}
|
||||
|
||||
ModelTracker modelTrkr = trackerRegistry.getModelTracker();
|
||||
TrackerState modelTrkrState = modelTrkr.getTrackerState();
|
||||
coreSummary.add("ModelTracker Active", modelTrkrState.isRunning());
|
||||
coreSummary.add("NodeState Publisher Active", trackerState.isRunning());
|
||||
|
||||
// TX
|
||||
|
||||
coreSummary.add("Last Index TX Commit Time", lastIndexTxCommitTime);
|
||||
coreSummary.add("Last Index TX Commit Date", lastIndexTxCommitDate);
|
||||
coreSummary.add("TX Lag", txLagSeconds + " s");
|
||||
coreSummary.add("TX Duration", txLag.toString());
|
||||
coreSummary.add("Timestamp for last TX on server", lastTxCommitTimeOnServer);
|
||||
coreSummary.add("Date for last TX on server", lastTxOnServerDate);
|
||||
coreSummary.add("Id for last TX on server", lastTxIdOnServer);
|
||||
coreSummary.add("Id for last TX in index", lastIndexedTxId);
|
||||
coreSummary.add("Approx transactions remaining", transactionsToDo);
|
||||
coreSummary.add("Approx transaction indexing time remaining", remainingTx.largestComponentformattedString());
|
||||
// Stats
|
||||
|
||||
coreSummary.add("Model sync times (ms)", srv.getTrackerStats().getModelTimes().getNamedList(detail, hist, values));
|
||||
coreSummary.add("Docs/Tx", srv.getTrackerStats().getTxDocs().getNamedList(detail, hist, values));
|
||||
|
||||
// Model
|
||||
|
||||
Map<String, Set<String>> modelErrors = srv.getModelErrors();
|
||||
if (modelErrors.size() > 0)
|
||||
{
|
||||
NamedList<Object> errorList = new SimpleOrderedMap<>();
|
||||
for (Map.Entry<String, Set<String>> modelNameToErrors : modelErrors.entrySet())
|
||||
{
|
||||
errorList.add(modelNameToErrors.getKey(), modelNameToErrors.getValue());
|
||||
}
|
||||
coreSummary.add("Model changes are not compatible with the existing data model and have not been applied", errorList);
|
||||
}
|
||||
|
||||
report.add(cname, coreSummary);
|
||||
}
|
||||
|
||||
static void addMasterOrStandaloneCoreSummary(TrackerRegistry trackerRegistry, String cname, boolean detail, boolean hist, boolean values,
|
||||
InformationServer srv, NamedList<Object> report) throws IOException
|
||||
{
|
||||
NamedList<Object> coreSummary = new SimpleOrderedMap<>();
|
||||
coreSummary.addAll((SimpleOrderedMap<Object>) srv.getCoreStats());
|
||||
|
||||
MetadataTracker metaTrkr = trackerRegistry.getTrackerForCore(cname, MetadataTracker.class);
|
||||
TrackerState metadataTrkrState = metaTrkr.getTrackerState();
|
||||
long lastIndexTxCommitTime = metadataTrkrState.getLastIndexedTxCommitTime();
|
||||
|
||||
long lastIndexedTxId = metadataTrkrState.getLastIndexedTxId();
|
||||
long lastTxCommitTimeOnServer = metadataTrkrState.getLastTxCommitTimeOnServer();
|
||||
long lastTxIdOnServer = metadataTrkrState.getLastTxIdOnServer();
|
||||
Date lastIndexTxCommitDate = new Date(lastIndexTxCommitTime);
|
||||
Date lastTxOnServerDate = new Date(lastTxCommitTimeOnServer);
|
||||
long transactionsToDo = lastTxIdOnServer - lastIndexedTxId;
|
||||
if (transactionsToDo < 0)
|
||||
{
|
||||
transactionsToDo = 0;
|
||||
}
|
||||
|
||||
AclTracker aclTrkr = trackerRegistry.getTrackerForCore(cname, AclTracker.class);
|
||||
TrackerState aclTrkrState = aclTrkr.getTrackerState();
|
||||
long lastIndexChangeSetCommitTime = aclTrkrState.getLastIndexedChangeSetCommitTime();
|
||||
long lastIndexedChangeSetId = aclTrkrState.getLastIndexedChangeSetId();
|
||||
long lastChangeSetCommitTimeOnServer = aclTrkrState.getLastChangeSetCommitTimeOnServer();
|
||||
long lastChangeSetIdOnServer = aclTrkrState.getLastChangeSetIdOnServer();
|
||||
Date lastIndexChangeSetCommitDate = new Date(lastIndexChangeSetCommitTime);
|
||||
Date lastChangeSetOnServerDate = new Date(lastChangeSetCommitTimeOnServer);
|
||||
long changeSetsToDo = lastChangeSetIdOnServer - lastIndexedChangeSetId;
|
||||
if (changeSetsToDo < 0)
|
||||
{
|
||||
changeSetsToDo = 0;
|
||||
}
|
||||
|
||||
long nodesToDo = 0;
|
||||
long remainingTxTimeMillis = 0;
|
||||
if (transactionsToDo > 0)
|
||||
{
|
||||
// We now use the elapsed time as seen by the single thread farming out metadata indexing
|
||||
double meanDocsPerTx = srv.getTrackerStats().getMeanDocsPerTx();
|
||||
double meanNodeElaspedIndexTime = srv.getTrackerStats().getMeanNodeElapsedIndexTime();
|
||||
nodesToDo = (long)(transactionsToDo * meanDocsPerTx);
|
||||
remainingTxTimeMillis = (long) (nodesToDo * meanNodeElaspedIndexTime);
|
||||
}
|
||||
Date now = new Date();
|
||||
Date end = new Date(now.getTime() + remainingTxTimeMillis);
|
||||
Duration remainingTx = new Duration(now, end);
|
||||
|
||||
long remainingChangeSetTimeMillis = 0;
|
||||
if (changeSetsToDo > 0)
|
||||
{
|
||||
// We now use the elapsed time as seen by the single thread farming out alc indexing
|
||||
double meanAclsPerChangeSet = srv.getTrackerStats().getMeanAclsPerChangeSet();
|
||||
double meanAclElapsedIndexTime = srv.getTrackerStats().getMeanAclElapsedIndexTime();
|
||||
remainingChangeSetTimeMillis = (long) (changeSetsToDo * meanAclsPerChangeSet * meanAclElapsedIndexTime);
|
||||
}
|
||||
now = new Date();
|
||||
end = new Date(now.getTime() + remainingChangeSetTimeMillis);
|
||||
Duration remainingChangeSet = new Duration(now, end);
|
||||
|
||||
NamedList<Object> ftsSummary = new SimpleOrderedMap<>();
|
||||
long remainingContentTimeMillis = 0;
|
||||
srv.addFTSStatusCounts(ftsSummary);
|
||||
long cleanCount =
|
||||
ofNullable(ftsSummary.get("Node count with FTSStatus Clean"))
|
||||
.map(Number.class::cast)
|
||||
.map(Number::longValue)
|
||||
.orElse(0L);
|
||||
long dirtyCount =
|
||||
ofNullable(ftsSummary.get("Node count with FTSStatus Dirty"))
|
||||
.map(Number.class::cast)
|
||||
.map(Number::longValue)
|
||||
.orElse(0L);
|
||||
long newCount =
|
||||
ofNullable(ftsSummary.get("Node count with FTSStatus New"))
|
||||
.map(Number.class::cast)
|
||||
.map(Number::longValue)
|
||||
.orElse(0L);
|
||||
|
||||
long nodesInIndex =
|
||||
ofNullable(coreSummary.get("Alfresco Nodes in Index"))
|
||||
.map(Number.class::cast)
|
||||
.map(Number::longValue)
|
||||
.orElse(0L);
|
||||
|
||||
long contentYetToSee = nodesInIndex > 0 ? nodesToDo * (cleanCount + dirtyCount + newCount)/nodesInIndex : 0;
|
||||
if (dirtyCount + newCount + contentYetToSee > 0)
|
||||
{
|
||||
// We now use the elapsed time as seen by the single thread farming out alc indexing
|
||||
double meanContentElapsedIndexTime = srv.getTrackerStats().getMeanContentElapsedIndexTime();
|
||||
remainingContentTimeMillis = (long) ((dirtyCount + newCount + contentYetToSee) * meanContentElapsedIndexTime);
|
||||
}
|
||||
now = new Date();
|
||||
end = new Date(now.getTime() + remainingContentTimeMillis);
|
||||
Duration remainingContent = new Duration(now, end);
|
||||
coreSummary.add("FTS",ftsSummary);
|
||||
|
||||
Duration txLag = new Duration(lastIndexTxCommitDate, lastTxOnServerDate);
|
||||
if (lastIndexTxCommitDate.compareTo(lastTxOnServerDate) > 0)
|
||||
{
|
||||
txLag = new Duration();
|
||||
}
|
||||
long txLagSeconds = (lastTxCommitTimeOnServer - lastIndexTxCommitTime) / 1000;
|
||||
if (txLagSeconds < 0)
|
||||
{
|
||||
txLagSeconds = 0;
|
||||
}
|
||||
|
||||
Duration changeSetLag = new Duration(lastIndexChangeSetCommitDate, lastChangeSetOnServerDate);
|
||||
if (lastIndexChangeSetCommitDate.compareTo(lastChangeSetOnServerDate) > 0)
|
||||
{
|
||||
changeSetLag = new Duration();
|
||||
}
|
||||
long changeSetLagSeconds = (lastChangeSetCommitTimeOnServer - lastIndexChangeSetCommitTime) / 1000;
|
||||
if (txLagSeconds < 0)
|
||||
{
|
||||
txLagSeconds = 0;
|
||||
}
|
||||
|
||||
ContentTracker contentTrkr = trackerRegistry.getTrackerForCore(cname, ContentTracker.class);
|
||||
TrackerState contentTrkrState = contentTrkr.getTrackerState();
|
||||
// Leave ModelTracker out of this check, because it is common
|
||||
boolean aTrackerIsRunning = aclTrkrState.isRunning() || metadataTrkrState.isRunning()
|
||||
|| contentTrkrState.isRunning();
|
||||
coreSummary.add("Active", aTrackerIsRunning);
|
||||
|
||||
ModelTracker modelTrkr = trackerRegistry.getModelTracker();
|
||||
TrackerState modelTrkrState = modelTrkr.getTrackerState();
|
||||
coreSummary.add("ModelTracker Active", modelTrkrState.isRunning());
|
||||
coreSummary.add("ContentTracker Active", contentTrkrState.isRunning());
|
||||
coreSummary.add("MetadataTracker Active", metadataTrkrState.isRunning());
|
||||
coreSummary.add("AclTracker Active", aclTrkrState.isRunning());
|
||||
|
||||
// TX
|
||||
|
||||
coreSummary.add("Last Index TX Commit Time", lastIndexTxCommitTime);
|
||||
coreSummary.add("Last Index TX Commit Date", lastIndexTxCommitDate);
|
||||
coreSummary.add("TX Lag", txLagSeconds + " s");
|
||||
coreSummary.add("TX Duration", txLag.toString());
|
||||
coreSummary.add("Timestamp for last TX on server", lastTxCommitTimeOnServer);
|
||||
coreSummary.add("Date for last TX on server", lastTxOnServerDate);
|
||||
coreSummary.add("Id for last TX on server", lastTxIdOnServer);
|
||||
coreSummary.add("Id for last TX in index", lastIndexedTxId);
|
||||
coreSummary.add("Approx transactions remaining", transactionsToDo);
|
||||
coreSummary.add("Approx transaction indexing time remaining", remainingTx.largestComponentformattedString());
|
||||
|
||||
// Change set
|
||||
|
||||
coreSummary.add("Last Index Change Set Commit Time", lastIndexChangeSetCommitTime);
|
||||
coreSummary.add("Last Index Change Set Commit Date", lastIndexChangeSetCommitDate);
|
||||
coreSummary.add("Change Set Lag", changeSetLagSeconds + " s");
|
||||
coreSummary.add("Change Set Duration", changeSetLag.toString());
|
||||
coreSummary.add("Timestamp for last Change Set on server", lastChangeSetCommitTimeOnServer);
|
||||
coreSummary.add("Date for last Change Set on server", lastChangeSetOnServerDate);
|
||||
coreSummary.add("Id for last Change Set on server", lastChangeSetIdOnServer);
|
||||
coreSummary.add("Id for last Change Set in index", lastIndexedChangeSetId);
|
||||
coreSummary.add("Approx change sets remaining", changeSetsToDo);
|
||||
coreSummary.add("Approx change set indexing time remaining",
|
||||
remainingChangeSet.largestComponentformattedString());
|
||||
|
||||
coreSummary.add("Approx content indexing time remaining",
|
||||
remainingContent.largestComponentformattedString());
|
||||
|
||||
// Stats
|
||||
|
||||
coreSummary.add("Model sync times (ms)",
|
||||
srv.getTrackerStats().getModelTimes().getNamedList(detail, hist, values));
|
||||
coreSummary.add("Acl index time (ms)",
|
||||
srv.getTrackerStats().getAclTimes().getNamedList(detail, hist, values));
|
||||
coreSummary.add("Node index time (ms)",
|
||||
srv.getTrackerStats().getNodeTimes().getNamedList(detail, hist, values));
|
||||
coreSummary.add("Docs/Tx", srv.getTrackerStats().getTxDocs().getNamedList(detail, hist, values));
|
||||
coreSummary.add("Doc Transformation time (ms)", srv.getTrackerStats().getDocTransformationTimes()
|
||||
.getNamedList(detail, hist, values));
|
||||
|
||||
// Model
|
||||
|
||||
Map<String, Set<String>> modelErrors = srv.getModelErrors();
|
||||
if (modelErrors.size() > 0)
|
||||
{
|
||||
NamedList<Object> errorList = new SimpleOrderedMap<>();
|
||||
for (Map.Entry<String, Set<String>> modelNameToErrors : modelErrors.entrySet())
|
||||
{
|
||||
errorList.add(modelNameToErrors.getKey(), modelNameToErrors.getValue());
|
||||
}
|
||||
coreSummary.add("Model changes are not compatible with the existing data model and have not been applied",
|
||||
errorList);
|
||||
}
|
||||
|
||||
report.add(cname, coreSummary);
|
||||
}
|
||||
}
|
||||
@@ -179,4 +179,6 @@ public interface InformationServer extends InformationServerCollectionProvider
|
||||
String getHostName();
|
||||
|
||||
String getBaseUrl();
|
||||
|
||||
void flushContentStore() throws IOException;
|
||||
}
|
||||
|
||||
+6
@@ -3892,4 +3892,10 @@ public class SolrInformationServer implements InformationServer
|
||||
}
|
||||
return searchers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushContentStore() throws IOException
|
||||
{
|
||||
solrContentStore.flushChangeSet();
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2019 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.solr.content;
|
||||
|
||||
import org.alfresco.solr.client.NodeMetaData;
|
||||
import org.apache.solr.common.SolrInputDocument;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Behavioural interface which indicates the type of content store access mode for the owning node.
|
||||
* A replication interaction is composed at least by 2 roles: a master and a slave.
|
||||
*
|
||||
* Despite the same interface, the behaviour of the content store management changes depending on the node kind:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Master Node: READ + WRITE + Changes tracking. Writes and changes tracking are a direct consequence of the "indexing" nature of the master node.</li>
|
||||
* <li>Slave Node: READ ONLY (i.e. never write: changes are applied on the master and replicated on slaves)</li>
|
||||
* </ul>
|
||||
*
|
||||
* Important: the owning entity *is not* the SolrCore instance: the Content store is shared across all cores of
|
||||
* A node can transit from Read to Write mode
|
||||
*
|
||||
* @author Andrea Gazzarini
|
||||
* @since 1.5
|
||||
*/
|
||||
interface AccessMode extends Closeable
|
||||
{
|
||||
/**
|
||||
* Returns the last persisted content store version.
|
||||
*
|
||||
* @return the last persisted content store version, SolrContentStore#NO_VERSION_AVAILABLE in case the version isn't available.
|
||||
*/
|
||||
long getLastCommittedVersion();
|
||||
|
||||
/**
|
||||
* Persists the last committed version on the hosting node.
|
||||
* Note that this is tipically valid only on slave node, because the master already manages the content store version on
|
||||
* a persistent storage, so it doesn't need to call this method.
|
||||
*
|
||||
* @param version the last committed content store version.
|
||||
*/
|
||||
void setLastCommittedVersion(long version);
|
||||
|
||||
Map<String, List<Map<String, Object>>> getChanges(long version);
|
||||
|
||||
/**
|
||||
* Stores a {@link SolrInputDocument} into Alfresco solr content store.
|
||||
*
|
||||
* @param tenant the owning tenant.
|
||||
* @param dbId the document DBID
|
||||
* @param doc the document itself.
|
||||
*/
|
||||
void storeDocOnSolrContentStore(String tenant, long dbId, SolrInputDocument doc);
|
||||
|
||||
/**
|
||||
* Stores a {@link SolrInputDocument} into Alfresco solr content store.
|
||||
*
|
||||
* @param nodeMetaData the node metadata.
|
||||
* @param doc the document itself.
|
||||
*/
|
||||
void storeDocOnSolrContentStore(NodeMetaData nodeMetaData, SolrInputDocument doc);
|
||||
|
||||
/**
|
||||
* Removes a node from the content store.
|
||||
*
|
||||
* @param nodeMetaData the node metadata.
|
||||
*/
|
||||
void removeDocFromContentStore(NodeMetaData nodeMetaData);
|
||||
|
||||
/**
|
||||
* Flushes pending changesets.
|
||||
*
|
||||
* @throws IOException in case of I/O failure.
|
||||
*/
|
||||
void flushChangeSet() throws IOException;
|
||||
|
||||
/**
|
||||
* Tries to transition from this mode to the readOnlyMode.
|
||||
*/
|
||||
void switchOnReadOnlyMode();
|
||||
|
||||
/**
|
||||
* Tries to transition from this mode to the read/write mode.
|
||||
*/
|
||||
void switchOnReadWriteMode();
|
||||
}
|
||||
+460
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2019 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.solr.content;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.Collections.emptySet;
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
import org.alfresco.util.Pair;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.LongPoint;
|
||||
import org.apache.lucene.document.NumericDocValuesField;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexWriterConfig;
|
||||
import org.apache.lucene.search.FieldDoc;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.ScoreDoc;
|
||||
import org.apache.lucene.search.SearcherManager;
|
||||
import org.apache.lucene.search.Sort;
|
||||
import org.apache.lucene.search.SortField;
|
||||
import org.apache.lucene.search.SortedNumericSortField;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.store.FSDirectory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BinaryOperator;
|
||||
|
||||
/**
|
||||
* Stores and manages changes occurred in a content store.
|
||||
*
|
||||
* Note this entity is part of the content store only when it is set on writable mode (i.e. only when the hosting node
|
||||
* is a standalone instance or it is a master node).
|
||||
*
|
||||
* That means if the hosting node has at least one standalone core or one master node, then the managed content store
|
||||
* will be set in write mode and therefore a {@link ChangeSet} instance will track all changes applied to it.
|
||||
*
|
||||
* In that mode, each change is associated to a given (incremental) version; changes consist of adds/updates and deletes.
|
||||
* Each time the {@link org.alfresco.solr.tracker.CommitTracker} executes a commit in the main index, all content store
|
||||
* changes accumulated in the meantime in the current {@link ChangeSet} instance are flushed in an auxiliary Lucene index
|
||||
* and therefore persisted.
|
||||
*
|
||||
* On the other side, when all cores on a given node are configured as slaves, the content store is in read-only mode,
|
||||
* and there's no need to track any change (i.e. changes are coming from master through the replication procedure).
|
||||
*
|
||||
* As a side note: we can have a persistent or transient {@link ChangeSet} instance. The first one is used to manage, as
|
||||
* the name suggests, in a persistent way the content store changes accumulated during the indexing operations (see the
|
||||
* description above about its behaviour in master nodes).
|
||||
*
|
||||
* The second one is used instead for computing/reducing the "merged" list of changes that we need to communicate to slave nodes:
|
||||
* as part of the replication mechanism, a slave communicates its last synched content store version; on the master side,
|
||||
* we need to compute and communicate back all changes that the slave needs to apply since that version in order to be in
|
||||
* synch with master.
|
||||
*
|
||||
* @author Andrea Gazzarini
|
||||
* @since 1.5
|
||||
*/
|
||||
public class ChangeSet implements AutoCloseable
|
||||
{
|
||||
private final static Logger LOGGER = LoggerFactory.getLogger(ChangeSet.class);
|
||||
private final static ChangeSet EMPTY_CHANGESET = new ChangeSet.Builder().empty().build();
|
||||
|
||||
/**
|
||||
* Builds class for creating {@link ChangeSet} instances.
|
||||
* The builder is here for creating three kind of {@link ChangeSet} instances:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Persistent: used for tracking and persisting content store changes.</li>
|
||||
* <li>Transient: used for computing the merged list of changes a slave should apply for being in synch with master.</li>
|
||||
* <li>Empty: an immutable "NullObject" used for denoting an empty {@link ChangeSet} instance.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Null_object_pattern">Null Object Design Pattern</a>
|
||||
*/
|
||||
public static class Builder
|
||||
{
|
||||
private String root;
|
||||
private boolean immutable;
|
||||
|
||||
/**
|
||||
* Adds a content store root folder to this builder.
|
||||
* This automatically implies we are creating a persistent {@link ChangeSet} instance.
|
||||
*
|
||||
* @param root the absolute path of the content store root folder.
|
||||
* @return this builder, for being used in a fluent mode.
|
||||
*/
|
||||
Builder withContentStoreRoot(String root)
|
||||
{
|
||||
if (root == null) throw new IllegalArgumentException("Unable to build the Changeset structures with a null content store root folder.");
|
||||
if (!new File(root).canWrite()) throw new IllegalArgumentException("Unable to build the Changeset structures with a non-writeable content store root folder.");
|
||||
|
||||
this.root = root;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* We are interested in building an empty, immutable {@link ChangeSet} instance.
|
||||
*
|
||||
* @return this builder, for being used in a fluent mode.
|
||||
*/
|
||||
Builder empty()
|
||||
{
|
||||
this.root = null;
|
||||
this.immutable = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the product of this builder (i.e. the {@link ChangeSet} instance).
|
||||
*
|
||||
* @return the product of this builder (i.e. the {@link ChangeSet} instance).
|
||||
*/
|
||||
ChangeSet build()
|
||||
{
|
||||
if (root == null)
|
||||
{
|
||||
// Creates a transient changeset (no persistence); mainly used for reducing the changes during replication.
|
||||
return new ChangeSet(
|
||||
immutable ? emptySet() : new HashSet<>(),
|
||||
immutable ? emptySet() : new HashSet<>());
|
||||
}
|
||||
|
||||
IndexWriter writer = null;
|
||||
try
|
||||
{
|
||||
File indexDirectory = new File(root, CHANGESETS_ROOT_FOLDER_NAME);
|
||||
|
||||
writer = new IndexWriter(FSDirectory.open(indexDirectory.toPath()), new IndexWriterConfig());
|
||||
writer.commit();
|
||||
|
||||
final SearcherManager searcher = new SearcherManager(writer, null);
|
||||
LOGGER.info("ContentStore Changeset index has been correctly mounted on {}", indexDirectory.getAbsolutePath());
|
||||
|
||||
return new ChangeSet(
|
||||
searcher,
|
||||
writer,
|
||||
immutable ? emptySet() : new HashSet<>(),
|
||||
immutable ? emptySet() : new HashSet<>());
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ofNullable(writer).ifPresent(ChangeSet::silentyClose);
|
||||
throw new IllegalArgumentException("Unable to create a ContentStore ChangeSet data structure. See further details in the stacktrack below.", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final static String VERSION_FIELD_NAME = "version";
|
||||
private final static String RVERSION_FIELD_NAME = "rversion";
|
||||
private final static String ADDS_FIELD_NAME = "adds";
|
||||
private final static String DELETES_FIELD_NAME = "deletes";
|
||||
final static String CHANGESETS_ROOT_FOLDER_NAME = "changeSets";
|
||||
|
||||
Set<String> deletes;
|
||||
Set<String> adds;
|
||||
|
||||
SearcherManager searcher;
|
||||
private final IndexWriter writer;
|
||||
|
||||
Query selectEverything = new MatchAllDocsQuery();
|
||||
|
||||
/**
|
||||
* Builds a new transient {@link ChangeSet} with the given Lucene facades.
|
||||
*
|
||||
* @param deletesContainer the container which will hold the deletes.
|
||||
* @param addsContainer the container which will hold the adds/updates.
|
||||
*/
|
||||
private ChangeSet(
|
||||
final Set<String> deletesContainer,
|
||||
final Set<String> addsContainer)
|
||||
{
|
||||
this(null, null, deletesContainer, addsContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new {@link ChangeSet} with the given Lucene facades.
|
||||
*
|
||||
* @param searcher the searcher reference (actually a {@link SearcherManager} instance instead of dealing with {@link IndexSearcher} directly.
|
||||
* @param writer the {@link IndexWriter} instance used for persisting the content store changes.
|
||||
* @param deletesContainer the container which will hold the deletes.
|
||||
* @param addsContainer the container which will hold the adds/updates.
|
||||
*/
|
||||
private ChangeSet(
|
||||
final SearcherManager searcher,
|
||||
final IndexWriter writer,
|
||||
final Set<String> deletesContainer,
|
||||
final Set<String> addsContainer)
|
||||
{
|
||||
this.searcher = searcher;
|
||||
this.writer = writer;
|
||||
this.deletes = deletesContainer;
|
||||
this.adds = addsContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Records a delete change.
|
||||
*
|
||||
* @param path the relative path of the file which has been deleted.
|
||||
*/
|
||||
synchronized void delete(String path)
|
||||
{
|
||||
adds.remove(path);
|
||||
deletes.add(path);
|
||||
|
||||
LOGGER.debug("ContentStore change recorded: item {} has been deleted.", path);
|
||||
|
||||
debugPendingChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Records an add or update change.
|
||||
*
|
||||
* @param path the relative path of the file which has been updated or added.
|
||||
*/
|
||||
synchronized void addOrReplace(String path)
|
||||
{
|
||||
deletes.remove(path);
|
||||
adds.add(path);
|
||||
|
||||
LOGGER.debug("ContentStore change recorded: item {} has been added/updated.", path);
|
||||
|
||||
debugPendingChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes all pending collected content store changes.
|
||||
*
|
||||
* @throws IOException in case of I/O failure.
|
||||
*/
|
||||
void flush() throws IOException
|
||||
{
|
||||
// No ops if this is a transient changeset
|
||||
if (searcher == null || writer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (adds.isEmpty() && deletes.isEmpty())
|
||||
{
|
||||
LOGGER.debug("No changes in contentstore to flush");
|
||||
return;
|
||||
}
|
||||
|
||||
final long version = System.currentTimeMillis();
|
||||
|
||||
LOGGER.debug("About to add a new Changeset entry (version = {}, deletes = {}, adds = {})", version, deletes.size(), adds.size());
|
||||
|
||||
final Document document = new Document();
|
||||
document.add(new NumericDocValuesField(VERSION_FIELD_NAME, version));
|
||||
document.add(new LongPoint(RVERSION_FIELD_NAME, version));
|
||||
|
||||
Set<String> tmpDel;
|
||||
Set<String> tmpAdd;
|
||||
|
||||
synchronized (this) {
|
||||
tmpDel = deletes;
|
||||
deletes = new HashSet<>();
|
||||
|
||||
tmpAdd = adds;
|
||||
adds = new HashSet<>();
|
||||
}
|
||||
|
||||
tmpDel.stream()
|
||||
.map(item -> new StoredField(DELETES_FIELD_NAME, item))
|
||||
.forEach(document::add);
|
||||
|
||||
tmpAdd.stream()
|
||||
.map(item -> new StoredField(ADDS_FIELD_NAME, item))
|
||||
.forEach(document::add);
|
||||
|
||||
writer.addDocument(document);
|
||||
writer.commit();
|
||||
searcher.maybeRefresh();
|
||||
|
||||
LOGGER.debug("New Changeset entry have been added (version = {}, deletes = {}, adds = {})", version, deletes.size(), adds.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanity check for making sure the requestor sent a valid and known content store version.
|
||||
*
|
||||
* @param version the content store version (sent by a requestor)
|
||||
* @return true if the given version is unknown, that is, is not part of the content store version history.
|
||||
*/
|
||||
boolean isUnknownVersion(long version)
|
||||
{
|
||||
try
|
||||
{
|
||||
TopDocs hits = searcher().search(LongPoint.newExactQuery(RVERSION_FIELD_NAME, version), 1);
|
||||
return hits.totalHits != 1;
|
||||
}
|
||||
catch(Exception exception)
|
||||
{
|
||||
LOGGER.error("Unable to check the requested version ({}) in the local versioning store. See further details in the stacktrace below.", version, exception);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
ofNullable(writer).ifPresent(ChangeSet::silentyClose);
|
||||
ofNullable(searcher).ifPresent(ChangeSet::silentyClose);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last persisted content store version.
|
||||
*
|
||||
* @return the last persisted content store version, SolrContentStore#NO_VERSION_AVAILABLE in case the version isn't available.
|
||||
*/
|
||||
long getLastCommittedVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
TopDocs hits = searcher().search(
|
||||
selectEverything,
|
||||
1,
|
||||
new Sort(new SortedNumericSortField(VERSION_FIELD_NAME, SortField.Type.LONG, true)),
|
||||
false,
|
||||
false);
|
||||
|
||||
return ofNullable(hits.scoreDocs)
|
||||
.filter(docs -> docs.length > 0)
|
||||
.map(docs -> ((FieldDoc) docs[0]).fields)
|
||||
.filter(fields -> fields.length > 0)
|
||||
.map(fields -> (Long)fields[0])
|
||||
.orElse(SolrContentStore.NO_VERSION_AVAILABLE);
|
||||
}
|
||||
catch(Exception exception)
|
||||
{
|
||||
LOGGER.error("Unable to retrieve the last committed content store changeset version. " +
|
||||
"As consequence of that a dummy value of " + SolrContentStore.NO_VERSION_AVAILABLE +
|
||||
" will be returned. See further details in the stacktrack below.",
|
||||
exception);
|
||||
return SolrContentStore.NO_VERSION_AVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content store changes (adds / deletes) since the given version (exclusive).
|
||||
*
|
||||
* @param version the start offset version (exclusive).
|
||||
* @return the content store changes (adds / deletes) since the given version (exclusive).
|
||||
*/
|
||||
public ChangeSet since(long version)
|
||||
{
|
||||
try
|
||||
{
|
||||
Query query = LongPoint.newRangeQuery(RVERSION_FIELD_NAME, Math.addExact(version, 1), Long.MAX_VALUE);
|
||||
TopDocs hits = searcher().search(
|
||||
query,
|
||||
100,
|
||||
new Sort(new SortedNumericSortField(VERSION_FIELD_NAME, SortField.Type.LONG)),
|
||||
false,
|
||||
false);
|
||||
|
||||
final BiFunction<ChangeSet, ? super Pair<List<String>,List<String>>, ChangeSet> accumulator =
|
||||
(partial, nth) -> {
|
||||
final List<String> nthDeletes = nth.getFirst();
|
||||
final List<String> nthAdds = nth.getSecond();
|
||||
|
||||
nthDeletes.forEach(partial::delete);
|
||||
nthAdds.forEach(partial::addOrReplace);
|
||||
|
||||
return partial;
|
||||
};
|
||||
|
||||
final BinaryOperator<ChangeSet> combiner = (c1, c2) -> {
|
||||
c1.deletes.forEach(c2::delete);
|
||||
c1.adds.forEach(c2::addOrReplace);
|
||||
return c1;
|
||||
};
|
||||
|
||||
return stream(hits.scoreDocs)
|
||||
.map(this::toDoc)
|
||||
.map(doc ->
|
||||
new Pair<>(
|
||||
asList(doc.getValues(DELETES_FIELD_NAME)),
|
||||
asList(doc.getValues(ADDS_FIELD_NAME))))
|
||||
.reduce(new ChangeSet.Builder().build(), accumulator, combiner);
|
||||
}
|
||||
catch(Exception exception)
|
||||
{
|
||||
LOGGER.error("Unable to retrieve the changeset since version {}. " +
|
||||
"As consequence of that an empty result will be returned. " +
|
||||
"See further details in the stacktrack below.",
|
||||
version,
|
||||
exception);
|
||||
return EMPTY_CHANGESET;
|
||||
}
|
||||
}
|
||||
|
||||
private Document toDoc(ScoreDoc hit)
|
||||
{
|
||||
try
|
||||
{
|
||||
return searcher().doc(hit.doc);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void debugPendingChanges()
|
||||
{
|
||||
if (LOGGER.isDebugEnabled())
|
||||
{
|
||||
LOGGER.debug("ContentStore pending deletes: " + deletes);
|
||||
LOGGER.debug("ContentStore pending adds/updates: " + adds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Silently close (i.e. without any exception re-throwing) the incoming resource.
|
||||
*
|
||||
* @param resource the closeable resource.
|
||||
*/
|
||||
private static void silentyClose(Closeable resource)
|
||||
{
|
||||
try
|
||||
{
|
||||
resource.close();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
LOGGER.error("Unable to properly close the resource instance {}. See further details in the stacktrace below.", resource, exception);
|
||||
}
|
||||
}
|
||||
|
||||
private IndexSearcher searcher() throws IOException
|
||||
{
|
||||
return searcher.acquire();
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2019 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.solr.content;
|
||||
|
||||
/**
|
||||
* {@link AccessMode} specialisation for those modes that need some kind of initialisation.
|
||||
*
|
||||
* @author Andrea Gazzarini
|
||||
* @since 1.5
|
||||
*/
|
||||
public interface InitialisableAccessMode extends AccessMode
|
||||
{
|
||||
/**
|
||||
* Initialises this access mode instance.
|
||||
*/
|
||||
void init();
|
||||
}
|
||||
+574
-173
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2014 Alfresco Software Limited.
|
||||
* Copyright (C) 2005-2019 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
@@ -18,52 +18,475 @@
|
||||
*/
|
||||
package org.alfresco.solr.content;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import org.alfresco.repo.content.ContentContext;
|
||||
import org.alfresco.repo.content.ContentStore;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.alfresco.solr.AlfrescoSolrDataModel;
|
||||
import org.alfresco.solr.client.NodeMetaData;
|
||||
import org.alfresco.solr.config.ConfigUtil;
|
||||
import org.alfresco.solr.handler.AlfrescoReplicationHandler;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.solr.common.SolrInputDocument;
|
||||
import org.apache.solr.common.util.JavaBinCodec;
|
||||
import org.apache.solr.core.SolrResourceLoader;
|
||||
import org.apache.solr.handler.SnapShooter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Optional.of;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.alfresco.solr.content.SolrContentUrlBuilder.FILE_EXTENSION;
|
||||
import static org.alfresco.solr.content.SolrContentUrlBuilder.logger;
|
||||
|
||||
/**
|
||||
* A content store specific to SOLR's requirements: The URL is generated from a
|
||||
* set of properties such as:
|
||||
*
|
||||
* <ul>
|
||||
* <li>ACL ID</li>
|
||||
* <li>DB ID</li>
|
||||
* <li>Other metadata</li>
|
||||
* <li>ACL ID</li>
|
||||
* <li>DB ID</li>
|
||||
* <li>Other metadata</li>
|
||||
* </ul>
|
||||
* The URL, if not known, can be reliably regenerated using the
|
||||
* {@link SolrContentUrlBuilder}.
|
||||
*
|
||||
*
|
||||
* The URL, if not known, can be reliably regenerated using the {@link SolrContentUrlBuilder}.
|
||||
* <br/>
|
||||
*
|
||||
* Since version 1.5 this class acts as a logical singleton: there will be only one instance per node.
|
||||
* That reflects exactly what we physically have in the filesystem (i.e. there's only one content store per node).
|
||||
*
|
||||
* That unique instance is created at startup in {@link org.alfresco.solr.AlfrescoCoreAdminHandler} and then passed to
|
||||
* each registered core (see {@link org.alfresco.solr.lifecycle.SolrCoreLoadListener})
|
||||
*
|
||||
* The State pattern implemented by means of the {@link AccessMode} interface allows for a given {@link SolrContentStore} instance to act:
|
||||
*
|
||||
* <ul>
|
||||
* <li>in READ/WRITE mode: when <b>at least one core</b> of the hosting node is a master or it is a standalone shard/instance.</li>
|
||||
* <li>in READ ONLY mode: when <b>all cores</b> of the hosting node are slaves.</li>
|
||||
* </ul>
|
||||
*
|
||||
* The Finite State Machine (FST) provides three possible states: Initial, Read Only, Read/Write.
|
||||
* The allowed transitions are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Initial -> ReadOnly: a slave core has been registered, the content store hasn't been yet initialised.</li>
|
||||
* <li>
|
||||
* ReadOnly -> Read/Write: this scenario is unusual because we should have on the same instance a mixed set of cores
|
||||
* (some master/standalone, some slaves). It happens when a first slave core registers and causes the transition
|
||||
* described in the first point (initial -> read only). Then a master or standalone core registers so we need to
|
||||
* move from a read only to a complete readable/writable managed content store.
|
||||
* </li>
|
||||
* <li> Initial -> Read/Write: a master or standalone core has been registered, and the content store hasn't been yet initialised.</li>
|
||||
* </ul>
|
||||
*
|
||||
* Note the following transitions are not allowed:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Coming back to Initial state: once it has been initialised the Content Store cannot return back to the "Initial" state.</li>
|
||||
* <li>
|
||||
* Read/Write -> ReadOnly: if at least one master or standalone core registers, the content store is permanently moved in Read/Write mode.
|
||||
* So even a further slave node registers (not very usually as described above) the content store remains in RW mode.
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @author Michael Suzuki
|
||||
* @since 5.0
|
||||
* @author Andrea Gazzarini
|
||||
* @since 1.5
|
||||
* @see org.alfresco.solr.lifecycle.SolrCoreLoadListener
|
||||
* @see <a href="https://it.wikipedia.org/wiki/State_pattern">State Pattern</a>
|
||||
*/
|
||||
public class SolrContentStore implements ContentStore
|
||||
public final class SolrContentStore implements Closeable, AccessMode
|
||||
{
|
||||
protected final static Logger log = LoggerFactory.getLogger(SolrContentStore.class);
|
||||
private final static Logger LOGGER = LoggerFactory.getLogger(SolrContentStore.class);
|
||||
|
||||
public static final long NO_VERSION_AVAILABLE = -1L;
|
||||
public static final long NO_CONTENT_STORE_REPLICATION_REQUIRED = -2L;
|
||||
|
||||
static final String CONTENT_STORE = "contentstore";
|
||||
static final String SOLR_CONTENT_DIR = "solr.content.dir";
|
||||
|
||||
public static final String INFO = "info";
|
||||
public static final String FULL_REPLICATION = "full-replication";
|
||||
public static final String DELETES = "deletes";
|
||||
public static final String ADDS = "adds";
|
||||
|
||||
private final Predicate<File> onlyDatafiles = file -> file.isFile() && file.getName().endsWith(FILE_EXTENSION);
|
||||
private final String root;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param solrHome
|
||||
* Used for denoting the very beginning state, when the {@link SolrContentStore} has been created
|
||||
* but we don't know (yet) which is the role played by the cores that will register on the hosting Solr node.
|
||||
*/
|
||||
AccessMode notYetSet = new AccessMode()
|
||||
{
|
||||
@Override
|
||||
public long getLastCommittedVersion()
|
||||
{
|
||||
throw new IllegalStateException("ContentStore hasn't been properly initialised.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastCommittedVersion(long version)
|
||||
{
|
||||
throw new IllegalStateException("ContentStore hasn't been properly initialised.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<Map<String, Object>>> getChanges(long version)
|
||||
{
|
||||
throw new IllegalStateException("ContentStore hasn't been properly initialised.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeDocOnSolrContentStore(String tenant, long dbId, SolrInputDocument doc)
|
||||
{
|
||||
throw new IllegalStateException("ContentStore hasn't been properly initialised.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeDocOnSolrContentStore(NodeMetaData nodeMetaData, SolrInputDocument doc)
|
||||
{
|
||||
throw new IllegalStateException("ContentStore hasn't been properly initialised.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDocFromContentStore(NodeMetaData nodeMetaData)
|
||||
{
|
||||
throw new IllegalStateException("ContentStore hasn't been properly initialised.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushChangeSet()
|
||||
{
|
||||
throw new IllegalStateException("ContentStore hasn't been properly initialised.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchOnReadOnlyMode()
|
||||
{
|
||||
logger.info("Switching the content store to ReadOnly mode.");
|
||||
currentAccessMode = readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchOnReadWriteMode()
|
||||
{
|
||||
logger.info("Switching the content store to Read/Write mode.");
|
||||
readWrite.init();
|
||||
currentAccessMode = readWrite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
// Nothing to close here
|
||||
}
|
||||
};
|
||||
|
||||
final InitialisableAccessMode readOnly = new InitialisableAccessMode()
|
||||
{
|
||||
@Override
|
||||
public void init()
|
||||
{
|
||||
// Nothing to be done here...
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchOnReadWriteMode()
|
||||
{
|
||||
logger.info("Switching from ReadOnly to Read/Write Content Store.");
|
||||
|
||||
readWrite.init();
|
||||
currentAccessMode = readWrite;
|
||||
|
||||
logger.info("Switching from ReadOnly to Read/Write Content Store.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchOnReadOnlyMode()
|
||||
{
|
||||
logger.info("The content store is already in ReadOnly mode so this call won't have any effect.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastCommittedVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Files.lines(Paths.get(root, ".version"))
|
||||
.map(Long::parseLong)
|
||||
.findFirst()
|
||||
.orElse(NO_VERSION_AVAILABLE);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return NO_VERSION_AVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastCommittedVersion(long version)
|
||||
{
|
||||
try
|
||||
{
|
||||
File tmpFile = new File(root, ".version-" + new SimpleDateFormat(SnapShooter.DATE_FMT, Locale.ROOT).format(new Date()));
|
||||
FileWriter wr = new FileWriter(tmpFile);
|
||||
wr.write(Long.toString(version));
|
||||
wr.close();
|
||||
|
||||
tmpFile.renameTo(new File(root, ".version"));
|
||||
}
|
||||
catch (IOException exception)
|
||||
{
|
||||
logger.error("Unable to persist the last committed content store version {}. See the stacktrace below for furtger details.", version, exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<Map<String, Object>>> getChanges(long version)
|
||||
{
|
||||
logger.warn("NoOp SolrContentStore changes call on slave side: this shouldn't happen because the ContentStore is in read-only mode when the hosting node is a slave.");
|
||||
return emptyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeDocOnSolrContentStore(String tenant, long dbId, SolrInputDocument doc)
|
||||
{
|
||||
logger.warn("NoOp SolrContentStore write call on slave side: this shouldn't happen because the ContentStore is in read-only mode when the hosting node is a slave.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeDocOnSolrContentStore(NodeMetaData nodeMetaData, SolrInputDocument doc)
|
||||
{
|
||||
logger.warn("NoOp SolrContentStore write call on slave side: this shouldn't happen because the ContentStore is in read-only mode when the hosting node is a slave.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDocFromContentStore(NodeMetaData nodeMetaData)
|
||||
{
|
||||
logger.warn("NoOp SolrContentStore write call on slave side: this shouldn't happen because the ContentStore is in read-only mode when the hosting node is a slave.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushChangeSet()
|
||||
{
|
||||
logger.warn("NoOp ChangeSet tracking call on slave side: this shouldn't happen because the ContentStore is in read-only mode when the hosting node is a slave.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
// There's nothing to close on slave side
|
||||
}
|
||||
};
|
||||
|
||||
final InitialisableAccessMode readWrite = new InitialisableAccessMode()
|
||||
{
|
||||
private ChangeSet changeSet;
|
||||
|
||||
@Override
|
||||
public void init()
|
||||
{
|
||||
changeSet = ofNullable(changeSet).orElseGet(() -> new ChangeSet.Builder().withContentStoreRoot(root).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastCommittedVersion()
|
||||
{
|
||||
return changeSet.getLastCommittedVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastCommittedVersion(long version)
|
||||
{
|
||||
// Do nothing here, as we are on master-side
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<Map<String, Object>>> getChanges(long version)
|
||||
{
|
||||
// The slave doesn't have a version, we are listing the whole content store
|
||||
if (version <= NO_VERSION_AVAILABLE || changeSet.isUnknownVersion(version))
|
||||
{
|
||||
String message = "A slave requested the content store synchronization " +
|
||||
((version <= NO_VERSION_AVAILABLE)
|
||||
? " without providing any local version (actually {})."
|
||||
: " with an invalid/unknown local version number ({}).") +
|
||||
"As consequence of that this master will list the whole content store because a full replication is needed.";
|
||||
|
||||
logger.info(message, version);
|
||||
|
||||
return Map.of(
|
||||
INFO, singletonList(Map.of(FULL_REPLICATION, true)),
|
||||
ADDS, fullContentStore(),
|
||||
DELETES, emptyList());
|
||||
}
|
||||
|
||||
ChangeSet changes = changeSet.since(version);
|
||||
|
||||
return Map.of(
|
||||
INFO, singletonList(Map.of(FULL_REPLICATION, false)),
|
||||
DELETES,
|
||||
changes.deletes.stream()
|
||||
.map(path -> Map.<String, Object>of("name", path))
|
||||
.collect(toList()),
|
||||
ADDS,
|
||||
changes.adds.stream()
|
||||
.map(relativePath -> root + relativePath)
|
||||
.map(File::new)
|
||||
.map(file -> new AlfrescoReplicationHandler.FileInfo(file, file.getAbsolutePath().replace(root, "")))
|
||||
.map(AlfrescoReplicationHandler.FileInfo::getAsMap)
|
||||
.collect(toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeDocOnSolrContentStore(String tenant, long dbId, SolrInputDocument doc)
|
||||
{
|
||||
ContentContext contentContext =
|
||||
of(SolrContentUrlBuilder
|
||||
.start()
|
||||
.add(SolrContentUrlBuilder.KEY_TENANT, tenant)
|
||||
.add(SolrContentUrlBuilder.KEY_DB_ID, String.valueOf(dbId)))
|
||||
.map(SolrContentUrlBuilder::getContentContext)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Unable to build a Content Context from tenant " + tenant + " and DBID " + dbId));
|
||||
|
||||
this.delete(contentContext.getContentUrl());
|
||||
|
||||
ContentWriter writer = this.getWriter(contentContext);
|
||||
|
||||
LOGGER.debug("Writing {}/{} to {}", tenant, dbId, contentContext.getContentUrl());
|
||||
|
||||
try (OutputStream contentOutputStream = writer.getContentOutputStream();
|
||||
GZIPOutputStream gzip = new GZIPOutputStream(contentOutputStream))
|
||||
{
|
||||
JavaBinCodec codec = new JavaBinCodec(resolver);
|
||||
codec.marshal(doc, gzip);
|
||||
|
||||
File file = getFileFromUrl(contentContext.getContentUrl());
|
||||
changeSet.addOrReplace(relativePath(file));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
LOGGER.warn("Unable to write to Content Store using URL: {}", contentContext.getContentUrl(), exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeDocOnSolrContentStore(NodeMetaData nodeMetaData, SolrInputDocument doc) {
|
||||
String fixedTenantDomain = AlfrescoSolrDataModel.getTenantId(nodeMetaData.getTenantDomain());
|
||||
storeDocOnSolrContentStore(fixedTenantDomain, nodeMetaData.getId(), doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDocFromContentStore(NodeMetaData nodeMetaData)
|
||||
{
|
||||
String fixedTenantDomain = AlfrescoSolrDataModel.getTenantId(nodeMetaData.getTenantDomain());
|
||||
String contentUrl = SolrContentUrlBuilder
|
||||
.start()
|
||||
.add(SolrContentUrlBuilder.KEY_TENANT, fixedTenantDomain)
|
||||
.add(SolrContentUrlBuilder.KEY_DB_ID, String.valueOf(nodeMetaData.getId()))
|
||||
.getContentContext()
|
||||
.getContentUrl();
|
||||
delete(contentUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushChangeSet() throws IOException
|
||||
{
|
||||
changeSet.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchOnReadWriteMode()
|
||||
{
|
||||
logger.debug("The content store is already in ReadWrite mode; as consequence of that, the incoming \"SET-TO-RW-MODE\" call won't have any effect.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchOnReadOnlyMode()
|
||||
{
|
||||
logger.debug("A writable content store cannot switch in ReadOnly mode. This could happen in an edge case where" +
|
||||
" on the same Solr node we have masters and slaves nodes");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
changeSet.close();
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> fullContentStore()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Files.walk(Paths.get(root))
|
||||
.map(Path::toFile)
|
||||
.filter(onlyDatafiles)
|
||||
.map(file -> new AlfrescoReplicationHandler.FileInfo(file, file.getAbsolutePath().replace(root, "")))
|
||||
.map(AlfrescoReplicationHandler.FileInfo::getAsMap)
|
||||
.collect(toList());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.error("An exception occurred while retrieving the whole ContentStore filelist. " +
|
||||
"As consequence of that an empty list will be returned (i.e. no ContentStore synch will happen).");
|
||||
return emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private void delete(String contentUrl)
|
||||
{
|
||||
File file = getFileFromUrl(contentUrl);
|
||||
if (file.delete()) changeSet.delete(relativePath(file));
|
||||
}
|
||||
|
||||
private ContentWriter getWriter(ContentContext context)
|
||||
{
|
||||
String url = context.getContentUrl();
|
||||
File file = getFileFromUrl(url);
|
||||
return new SolrFileContentWriter(file, url);
|
||||
}
|
||||
};
|
||||
|
||||
AccessMode currentAccessMode = notYetSet;
|
||||
|
||||
private final JavaBinCodec.ObjectResolver resolver = (o, codec) -> {
|
||||
if(o instanceof BytesRef)
|
||||
{
|
||||
BytesRef br = (BytesRef)o;
|
||||
codec.writeByteArray(br.bytes,br.offset,br.length);
|
||||
return null;
|
||||
}
|
||||
return o;
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds a new {@link SolrContentStore} instance with the given SOLR HOME.
|
||||
*
|
||||
* @param solrHome the Solr HOME.
|
||||
*/
|
||||
public SolrContentStore(String solrHome)
|
||||
{
|
||||
@@ -77,122 +500,92 @@ public class SolrContentStore implements ContentStore
|
||||
{
|
||||
//Its very unlikely that solrHome would not exist so we will log an error
|
||||
//but continue because solr.content.dir may be specified, so it keeps working
|
||||
log.error(solrHomeFile.getAbsolutePath() + " does not exist.");
|
||||
LOGGER.error(solrHomeFile.getAbsolutePath() + " does not exist.");
|
||||
}
|
||||
|
||||
String path = solrHomeFile.getParent()+"/"+CONTENT_STORE;
|
||||
log.warn(path + " will be used as a default path if " + SOLR_CONTENT_DIR + " property is not defined");
|
||||
|
||||
String path = solrHomeFile.getParent() + "/" + CONTENT_STORE;
|
||||
LOGGER.warn(path + " will be used as a default path if " + SOLR_CONTENT_DIR + " property is not defined");
|
||||
File rootFile = new File(ConfigUtil.locateProperty(SOLR_CONTENT_DIR, path));
|
||||
|
||||
try
|
||||
{
|
||||
FileUtils.forceMkdir(rootFile);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Failed to create directory for content store: " + rootFile, e);
|
||||
}
|
||||
|
||||
this.root = rootFile.getAbsolutePath();
|
||||
}
|
||||
|
||||
// write a BytesRef as a byte array
|
||||
private JavaBinCodec.ObjectResolver resolver = new JavaBinCodec.ObjectResolver()
|
||||
/**
|
||||
* Returns the content store changes since the given (requestor) version.
|
||||
*
|
||||
* @param version the requestor version.
|
||||
* @return the content store changes since the given (requestor) version.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, List<Map<String, Object>>> getChanges(long version)
|
||||
{
|
||||
@Override public Object resolve(Object o,JavaBinCodec codec)throws IOException
|
||||
{
|
||||
if(o instanceof BytesRef)
|
||||
{
|
||||
BytesRef br=(BytesRef)o;
|
||||
codec.writeByteArray(br.bytes,br.offset,br.length);
|
||||
return null;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
};
|
||||
return currentAccessMode.getChanges(version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve document from SolrContentStore.
|
||||
*
|
||||
* @param tenant identifier
|
||||
* @param dbId identifier
|
||||
* @return {@link SolrInputDocument} searched document
|
||||
* @throws IOException if error
|
||||
*/
|
||||
public SolrInputDocument retrieveDocFromSolrContentStore(String tenant, long dbId) throws IOException
|
||||
public SolrInputDocument retrieveDocFromSolrContentStore(String tenant, long dbId)
|
||||
{
|
||||
String contentUrl = SolrContentUrlBuilder.start().add(SolrContentUrlBuilder.KEY_TENANT, tenant)
|
||||
.add(SolrContentUrlBuilder.KEY_DB_ID, String.valueOf(dbId)).get();
|
||||
String contentUrl =
|
||||
SolrContentUrlBuilder.start()
|
||||
.add(SolrContentUrlBuilder.KEY_TENANT, tenant)
|
||||
.add(SolrContentUrlBuilder.KEY_DB_ID, String.valueOf(dbId))
|
||||
.get();
|
||||
|
||||
ContentReader reader = this.getReader(contentUrl);
|
||||
SolrInputDocument cachedDoc = null;
|
||||
if (reader.exists())
|
||||
if (!reader.exists())
|
||||
{
|
||||
// try-with-resources statement closes all these InputStreams
|
||||
try (InputStream contentInputStream = reader.getContentInputStream();
|
||||
// Uncompresses the document
|
||||
GZIPInputStream gzip = new GZIPInputStream(contentInputStream);)
|
||||
{
|
||||
cachedDoc = (SolrInputDocument) new JavaBinCodec(resolver).unmarshal(gzip);
|
||||
} catch (Exception e)
|
||||
{
|
||||
// Don't fail for this
|
||||
log.warn("Failed to get doc from store using URL: " + contentUrl, e);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
try (InputStream contentInputStream = reader.getContentInputStream();
|
||||
InputStream gzip = new GZIPInputStream(contentInputStream))
|
||||
{
|
||||
return (SolrInputDocument) new JavaBinCodec(resolver).unmarshal(gzip);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
// Don't fail for this
|
||||
LOGGER.warn("Failed to get doc from store using URL: " + contentUrl, exception);
|
||||
return null;
|
||||
}
|
||||
return cachedDoc;
|
||||
}
|
||||
|
||||
private final String root;
|
||||
|
||||
@Override
|
||||
public boolean isContentUrlSupported(String contentUrl)
|
||||
public long getLastCommittedVersion()
|
||||
{
|
||||
return (contentUrl != null && contentUrl.startsWith(SolrContentUrlBuilder.SOLR_PROTOCOL_PREFIX));
|
||||
return currentAccessMode.getLastCommittedVersion();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return <tt>true</tt> always
|
||||
*/
|
||||
@Override
|
||||
public boolean isWriteSupported()
|
||||
public void setLastCommittedVersion(long version)
|
||||
{
|
||||
return true;
|
||||
currentAccessMode.setLastCommittedVersion(version);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return -1 always
|
||||
* Returns the absolute path of the content store root folder.
|
||||
*
|
||||
* @return the absolute path of the content store root folder.
|
||||
*/
|
||||
@Override
|
||||
public long getSpaceFree()
|
||||
{
|
||||
return -1L;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return -1 always
|
||||
*/
|
||||
@Override
|
||||
public long getSpaceTotal()
|
||||
{
|
||||
return -1L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRootLocation()
|
||||
{
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a content URL into a File, whether it exists or not
|
||||
*/
|
||||
private File getFileFromUrl(String contentUrl)
|
||||
{
|
||||
String path = contentUrl.replace(SolrContentUrlBuilder.SOLR_PROTOCOL_PREFIX, root + "/");
|
||||
return new File(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String contentUrl)
|
||||
{
|
||||
File file = getFileFromUrl(contentUrl);
|
||||
@@ -200,93 +593,101 @@ public class SolrContentStore implements ContentStore
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentReader getReader(String contentUrl)
|
||||
public void storeDocOnSolrContentStore(String tenant, long dbId, SolrInputDocument doc)
|
||||
{
|
||||
currentAccessMode.storeDocOnSolrContentStore(tenant, dbId, doc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store {@link SolrInputDocument} in to Alfresco solr content store.
|
||||
*
|
||||
* @param nodeMetaData the incoming node metadata.
|
||||
* @param doc the document itself.
|
||||
*/
|
||||
@Override
|
||||
public void storeDocOnSolrContentStore(NodeMetaData nodeMetaData, SolrInputDocument doc)
|
||||
{
|
||||
currentAccessMode.storeDocOnSolrContentStore(nodeMetaData, doc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes {@link SolrInputDocument} from Alfresco solr content store.
|
||||
*
|
||||
* @param nodeMetaData the incoming node metadata.
|
||||
*/
|
||||
@Override
|
||||
public void removeDocFromContentStore(NodeMetaData nodeMetaData)
|
||||
{
|
||||
currentAccessMode.removeDocFromContentStore(nodeMetaData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushChangeSet() throws IOException
|
||||
{
|
||||
currentAccessMode.flushChangeSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
currentAccessMode.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchOnReadWriteMode()
|
||||
{
|
||||
currentAccessMode = readWrite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchOnReadOnlyMode()
|
||||
{
|
||||
currentAccessMode = readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assuming the input file belongs to the content store, it returns the corresponding relative path.
|
||||
*
|
||||
* @param file the content store file.
|
||||
* @return the relative file path.
|
||||
*/
|
||||
private String relativePath(File file)
|
||||
{
|
||||
return file.getAbsolutePath().replace(root, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a content URL into a File, whether it exists or not
|
||||
*/
|
||||
private File getFileFromUrl(String contentUrl)
|
||||
{
|
||||
return new File(contentUrl.replace(SolrContentUrlBuilder.SOLR_PROTOCOL_PREFIX, root + "/"));
|
||||
}
|
||||
|
||||
private ContentReader getReader(String contentUrl)
|
||||
{
|
||||
File file = getFileFromUrl(contentUrl);
|
||||
return new SolrFileContentReader(file, contentUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentWriter getWriter(ContentContext context)
|
||||
{
|
||||
// Ensure that there is a context and that it has a URL
|
||||
if (context == null || context.getContentUrl() == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Retrieve a writer with a URL-providing ContentContext.");
|
||||
}
|
||||
String url = context.getContentUrl();
|
||||
File file = getFileFromUrl(url);
|
||||
SolrFileContentWriter writer = new SolrFileContentWriter(file, url);
|
||||
// Done
|
||||
return writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(String contentUrl)
|
||||
{
|
||||
File file = getFileFromUrl(contentUrl);
|
||||
return file.delete();
|
||||
}
|
||||
/**
|
||||
* Stores a {@link SolrInputDocument} into Alfresco solr content store.
|
||||
* @param tenant
|
||||
* @param dbId
|
||||
* @param doc
|
||||
* @throws IOException
|
||||
* Enables/disables the content store access mode.
|
||||
* The term "toggles" is just for indicating that this method will be called several times (one for each registered
|
||||
* core). The underlying FSM makes sure this call will be idempotent so in case of read/write content store the
|
||||
* data structure used for maintaining the content store versioning will be initialised only once, even if this
|
||||
* method is called repeatedly.
|
||||
*
|
||||
* @param enableReadOnlyMode a flag indicating if the requesting core requires a readOnly (true) or readWrite (false) content store.
|
||||
*/
|
||||
public void storeDocOnSolrContentStore(String tenant, long dbId, SolrInputDocument doc) throws IOException
|
||||
public synchronized void toggleReadOnlyMode(boolean enableReadOnlyMode)
|
||||
{
|
||||
ContentContext contentContext = SolrContentUrlBuilder
|
||||
.start()
|
||||
.add(SolrContentUrlBuilder.KEY_TENANT, tenant)
|
||||
.add(SolrContentUrlBuilder.KEY_DB_ID, String.valueOf(dbId))
|
||||
.getContentContext();
|
||||
this.delete(contentContext.getContentUrl());
|
||||
ContentWriter writer = this.getWriter(contentContext);
|
||||
if (log.isDebugEnabled())
|
||||
if (enableReadOnlyMode)
|
||||
{
|
||||
log.debug("Writing doc to " + contentContext.getContentUrl());
|
||||
currentAccessMode.switchOnReadOnlyMode();
|
||||
}
|
||||
try (
|
||||
OutputStream contentOutputStream = writer.getContentOutputStream();
|
||||
// Compresses the document
|
||||
GZIPOutputStream gzip = new GZIPOutputStream(contentOutputStream);
|
||||
)
|
||||
else
|
||||
{
|
||||
JavaBinCodec codec = new JavaBinCodec(resolver);
|
||||
codec.marshal(doc, gzip);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// A failure to write to the store is acceptable as long as it's logged
|
||||
log.warn("Failed to write to store using URL: " + contentContext.getContentUrl(), e);
|
||||
currentAccessMode.switchOnReadWriteMode();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Store {@link SolrInputDocument} in to Alfresco solr content store.
|
||||
* @param nodeMetaData identifier
|
||||
* @param doc to store
|
||||
* @throws IOException if error
|
||||
*/
|
||||
public void storeDocOnSolrContentStore(NodeMetaData nodeMetaData, SolrInputDocument doc) throws IOException
|
||||
{
|
||||
String fixedTenantDomain = AlfrescoSolrDataModel.getTenantId(nodeMetaData.getTenantDomain());
|
||||
storeDocOnSolrContentStore(fixedTenantDomain, nodeMetaData.getId(), doc);
|
||||
}
|
||||
/**
|
||||
* Removes {@link SolrInputDocument} from Alfresco solr content store.
|
||||
* @param nodeMetaData
|
||||
*/
|
||||
public void removeDocFromContentStore(NodeMetaData nodeMetaData)
|
||||
{
|
||||
String fixedTenantDomain = AlfrescoSolrDataModel.getTenantId(nodeMetaData.getTenantDomain());
|
||||
String contentUrl = SolrContentUrlBuilder
|
||||
.start()
|
||||
.add(SolrContentUrlBuilder.KEY_TENANT, fixedTenantDomain)
|
||||
.add(SolrContentUrlBuilder.KEY_DB_ID, String.valueOf(nodeMetaData.getId()))
|
||||
.getContentContext()
|
||||
.getContentUrl();
|
||||
this.delete(contentUrl);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+28
-41
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2014 Alfresco Software Limited.
|
||||
* Copyright (C) 2005-2019 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
package org.alfresco.solr.content;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.zip.CRC32;
|
||||
@@ -43,20 +43,13 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
public class SolrContentUrlBuilder
|
||||
{
|
||||
/**
|
||||
* <b>solr</b> is the prefix for SOLR content URLs
|
||||
* @see #isContentUrlSupported(String)
|
||||
*/
|
||||
public static final String SOLR_PROTOCOL = "solr";
|
||||
public static final String SOLR_PROTOCOL_PREFIX = SOLR_PROTOCOL + ContentStore.PROTOCOL_DELIMITER;
|
||||
public static final String FILE_EXTENSION = ".gz";
|
||||
private static final String SOLR_PROTOCOL = "solr";
|
||||
static final String SOLR_PROTOCOL_PREFIX = SOLR_PROTOCOL + ContentStore.PROTOCOL_DELIMITER;
|
||||
static final String FILE_EXTENSION = ".gz";
|
||||
|
||||
/** The key for the tenant name */
|
||||
public static final String KEY_TENANT = "tenant";
|
||||
/** The key for the DB ID */
|
||||
public static final String KEY_DB_ID = "dbId";
|
||||
/** The key for the ACL ID */
|
||||
public static final String KEY_ACL_ID = "aclId";
|
||||
static final String KEY_TENANT = "tenant";
|
||||
static final String KEY_DB_ID = "dbId";
|
||||
static final String KEY_ACL_ID = "aclId";
|
||||
|
||||
protected final static Logger logger = LoggerFactory.getLogger(SolrContentUrlBuilder.class);
|
||||
|
||||
@@ -66,9 +59,9 @@ public class SolrContentUrlBuilder
|
||||
/**
|
||||
* Protected constructor used by {@link SolrContentUrlBuilder#start()}
|
||||
*/
|
||||
protected SolrContentUrlBuilder()
|
||||
private SolrContentUrlBuilder()
|
||||
{
|
||||
this.metadata = new TreeMap<String, String>();
|
||||
this.metadata = new TreeMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,32 +76,34 @@ public class SolrContentUrlBuilder
|
||||
|
||||
/**
|
||||
* Add some metadata to the URL generator. The order in which metadata is added is irrelevant.
|
||||
* <p/>
|
||||
* Note that there are specific keys that are commonly used and, if provided, may not be null or empty.
|
||||
*
|
||||
* <ul>
|
||||
* <li><b>{@link #KEY_TENANT}:</b> The name of the tenant or 'default' if missing.</li>
|
||||
* <li><b>{@link #KEY_DB_ID}:</b> The database ID.</li>
|
||||
* <li><b>{@link #KEY_ACL_ID}:</b> The ACL ID.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param key an arbitrary metadata key (never <tt>null</tt>>
|
||||
* @param value some metadata value (<tt>null</tt> is supported)
|
||||
* @return this builder for more building
|
||||
* @param key an arbitrary metadata key (never <tt>null</tt>>
|
||||
* @param value some metadata value (<tt>null</tt> is supported)
|
||||
* @return this builder for more building
|
||||
*
|
||||
* @throws IllegalArgumentException if the key is null
|
||||
* @throws IllegalStateException if the key has been used already
|
||||
* @throws IllegalArgumentException if the key is null
|
||||
* @throws IllegalStateException if the key has been used already
|
||||
*/
|
||||
public synchronized SolrContentUrlBuilder add(String key, String value)
|
||||
public SolrContentUrlBuilder add(String key, String value)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new IllegalArgumentException("The metadata 'key' may not be null.");
|
||||
}
|
||||
|
||||
String previous = metadata.put(key, value);
|
||||
if (previous != null)
|
||||
{
|
||||
throw new IllegalStateException("The metadata key, '" + key + "', has already been used.");
|
||||
}
|
||||
|
||||
// Check well-known keys
|
||||
if (key.equals(KEY_TENANT) || key.equals(KEY_DB_ID) || key.equals(KEY_ACL_ID))
|
||||
{
|
||||
@@ -131,8 +126,8 @@ public class SolrContentUrlBuilder
|
||||
/**
|
||||
* Get the final content URL using the {@link #add(String, String) supplied metadata}.
|
||||
*
|
||||
* @return the SOLR content URL
|
||||
* @throws IllegalStateException if no metadata has been added
|
||||
* @return the SOLR content URL
|
||||
* @throws IllegalStateException if no metadata has been added
|
||||
*/
|
||||
public synchronized String get()
|
||||
{
|
||||
@@ -169,21 +164,13 @@ public class SolrContentUrlBuilder
|
||||
sb.append("misc/");
|
||||
// Calculate the CRC
|
||||
CRC32 crc = new CRC32();
|
||||
try
|
||||
for (Map.Entry<String, String> entry : metadata.entrySet())
|
||||
{
|
||||
for (Map.Entry<String, String> entry : metadata.entrySet())
|
||||
{
|
||||
// This is ordered, so just add each entry as "key = value".
|
||||
// DO NOT USE entry.toString() because the format is not a contract
|
||||
// and we have to have the same string for the same metadata
|
||||
String entryStr = entry.getKey() + "=" + entry.getValue() + "; ";
|
||||
crc.update(entryStr.getBytes("UTF-8"));
|
||||
}
|
||||
}
|
||||
catch (UnsupportedEncodingException e)
|
||||
{
|
||||
// Yeah, right.
|
||||
throw new RuntimeException("UTF-8 is not supported.", e);
|
||||
// This is ordered, so just add each entry as "key = value".
|
||||
// DO NOT USE entry.toString() because the format is not a contract
|
||||
// and we have to have the same string for the same metadata
|
||||
String entryStr = entry.getKey() + "=" + entry.getValue() + "; ";
|
||||
crc.update(entryStr.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
numSb.append(crc.getValue());
|
||||
}
|
||||
@@ -218,7 +205,7 @@ public class SolrContentUrlBuilder
|
||||
/**
|
||||
* Helper method to retrieve a {@link ContentContext} constructed using the final {@link #get()} url.
|
||||
*/
|
||||
public ContentContext getContentContext()
|
||||
ContentContext getContentContext()
|
||||
{
|
||||
String url = get();
|
||||
return new ContentContext(null, url);
|
||||
|
||||
+7
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2014 Alfresco Software Limited.
|
||||
* Copyright (C) 2005-2019 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
@@ -42,7 +42,7 @@ import org.springframework.util.FileCopyUtils;
|
||||
* @author Derek Hulley
|
||||
* @since 5.0
|
||||
*/
|
||||
public class SolrFileContentReader implements ContentReader
|
||||
class SolrFileContentReader implements ContentReader
|
||||
{
|
||||
private final File file;
|
||||
private final String contentUrl;
|
||||
@@ -51,7 +51,7 @@ public class SolrFileContentReader implements ContentReader
|
||||
* @param file the file to write to
|
||||
* @param contentUrl the content URL for information purposes
|
||||
*/
|
||||
protected SolrFileContentReader(File file, String contentUrl)
|
||||
SolrFileContentReader(File file, String contentUrl)
|
||||
{
|
||||
this.file = file;
|
||||
this.contentUrl = contentUrl;
|
||||
@@ -120,9 +120,8 @@ public class SolrFileContentReader implements ContentReader
|
||||
}
|
||||
try
|
||||
{
|
||||
InputStream is = new BufferedInputStream(new FileInputStream(file));
|
||||
// done
|
||||
return is;
|
||||
return new BufferedInputStream(new FileInputStream(file));
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
@@ -192,12 +191,9 @@ public class SolrFileContentReader implements ContentReader
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
FileCopyUtils.copy(is, os); // both streams are closed
|
||||
byte[] bytes = os.toByteArray();
|
||||
// get the encoding for the string
|
||||
|
||||
String encoding = "UTF-8";
|
||||
// create the string from the byte[] using encoding if necessary
|
||||
String content = (encoding == null) ? new String(bytes) : new String(bytes, encoding);
|
||||
// done
|
||||
return content;
|
||||
return new String(bytes, encoding);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
@@ -266,4 +262,4 @@ public class SolrFileContentReader implements ContentReader
|
||||
{
|
||||
throw new UnsupportedOperationException("Auto-created method not implemented.");
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2014 Alfresco Software Limited.
|
||||
* Copyright (C) 2005-2019 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
@@ -41,7 +41,7 @@ import org.apache.commons.io.FileUtils;
|
||||
* @author Derek Hulley
|
||||
* @since 5.0
|
||||
*/
|
||||
public class SolrFileContentWriter implements ContentWriter
|
||||
class SolrFileContentWriter implements ContentWriter
|
||||
{
|
||||
private final File file;
|
||||
private final String contentUrl;
|
||||
@@ -51,11 +51,10 @@ public class SolrFileContentWriter implements ContentWriter
|
||||
* @param file the file to write to
|
||||
* @param contentUrl the content URL for information purposes
|
||||
*/
|
||||
protected SolrFileContentWriter(File file, String contentUrl)
|
||||
SolrFileContentWriter(File file, String contentUrl)
|
||||
{
|
||||
this.file = file;
|
||||
this.contentUrl = contentUrl;
|
||||
this.written = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,7 +108,7 @@ public class SolrFileContentWriter implements ContentWriter
|
||||
@Override
|
||||
public synchronized OutputStream getContentOutputStream() throws ContentIOException
|
||||
{
|
||||
if (written == true)
|
||||
if (written)
|
||||
{
|
||||
throw new IllegalStateException("The writer has already been used: " + file);
|
||||
}
|
||||
@@ -117,6 +116,7 @@ public class SolrFileContentWriter implements ContentWriter
|
||||
{
|
||||
throw new IllegalStateException("The file already exists: " + file);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
OutputStream is = new BufferedOutputStream(FileUtils.openOutputStream(file));
|
||||
@@ -139,7 +139,7 @@ public class SolrFileContentWriter implements ContentWriter
|
||||
@Override
|
||||
public synchronized void putContent(InputStream is) throws ContentIOException
|
||||
{
|
||||
if (written == true)
|
||||
if (written)
|
||||
{
|
||||
throw new IllegalStateException("The writer has already been used: " + file);
|
||||
}
|
||||
@@ -147,6 +147,7 @@ public class SolrFileContentWriter implements ContentWriter
|
||||
{
|
||||
throw new IllegalStateException("The file already exists: " + file);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
FileUtils.copyInputStreamToFile(is, file);
|
||||
@@ -161,7 +162,7 @@ public class SolrFileContentWriter implements ContentWriter
|
||||
@Override
|
||||
public synchronized void putContent(File sourceFile) throws ContentIOException
|
||||
{
|
||||
if (written == true)
|
||||
if (written)
|
||||
{
|
||||
throw new IllegalStateException("The writer has already been used: " + this.file);
|
||||
}
|
||||
@@ -173,6 +174,7 @@ public class SolrFileContentWriter implements ContentWriter
|
||||
{
|
||||
throw new IllegalStateException("The source file does not exist: " + sourceFile);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
FileUtils.copyFile(sourceFile, this.file, false);
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2019 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/>.
|
||||
*/
|
||||
/**
|
||||
* The package contains all components that manage the Solr ContentStore.
|
||||
* Note that in SearchServices 2.0 the whole package will be deprecated/removed because the content store will be
|
||||
* replaced by the built-in Solr storage capability (i.e. stored fields).
|
||||
*/
|
||||
package org.alfresco.solr.content;
|
||||
+2743
File diff suppressed because it is too large
Load Diff
+2374
File diff suppressed because it is too large
Load Diff
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* Modification copyright (C) 2005-2019 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.solr.handler;
|
||||
|
||||
import org.apache.solr.handler.SnapShooter;
|
||||
|
||||
import java.net.URI;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
class OldBackupDirectory implements Comparable<OldBackupDirectory>
|
||||
{
|
||||
private static final Pattern dirNamePattern = Pattern.compile("^snapshot[.](.*)$");
|
||||
|
||||
private URI basePath;
|
||||
private String dirName;
|
||||
private Optional<Date> timestamp = Optional.empty();
|
||||
|
||||
OldBackupDirectory(URI basePath, String dirName)
|
||||
{
|
||||
this.dirName = Objects.requireNonNull(dirName);
|
||||
this.basePath = Objects.requireNonNull(basePath);
|
||||
Matcher m = dirNamePattern.matcher(dirName);
|
||||
if (m.find())
|
||||
{
|
||||
try
|
||||
{
|
||||
this.timestamp = Optional.of(new SimpleDateFormat(SnapShooter.DATE_FMT, Locale.ROOT).parse(m.group(1)));
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
this.timestamp = Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public URI getPath()
|
||||
{
|
||||
return this.basePath.resolve(dirName);
|
||||
}
|
||||
|
||||
String getDirName()
|
||||
{
|
||||
return dirName;
|
||||
}
|
||||
|
||||
public Optional<Date> getTimestamp()
|
||||
{
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(OldBackupDirectory that)
|
||||
{
|
||||
if (this.timestamp.isPresent() && that.timestamp.isPresent())
|
||||
{
|
||||
return that.timestamp.get().compareTo(this.timestamp.get());
|
||||
}
|
||||
// Use absolute value of path in case the time-stamp is missing on either side.
|
||||
return that.getPath().compareTo(this.getPath());
|
||||
}
|
||||
}
|
||||
+377
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* Modification copyright (C) 2005-2019 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.solr.handler;
|
||||
|
||||
import org.apache.lucene.index.IndexCommit;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.common.SolrException.ErrorCode;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.core.DirectoryFactory.DirContext;
|
||||
import org.apache.solr.core.IndexDeletionPolicyWrapper;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.core.backup.repository.BackupRepository;
|
||||
import org.apache.solr.core.backup.repository.BackupRepository.PathType;
|
||||
import org.apache.solr.core.backup.repository.LocalFileSystemRepository;
|
||||
import org.apache.solr.core.snapshots.SolrSnapshotMetaDataManager;
|
||||
import org.apache.solr.search.SolrIndexSearcher;
|
||||
import org.apache.solr.util.RefCounted;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* <p> Provides functionality equivalent to the snapshooter script </p>
|
||||
* This is no longer used in standard replication.
|
||||
*
|
||||
* @since solr 1.4
|
||||
*/
|
||||
public class SnapShooter
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
private SolrCore solrCore;
|
||||
private String snapshotName = null;
|
||||
private String directoryName = null;
|
||||
private URI baseSnapDirPath = null;
|
||||
private URI snapshotDirPath = null;
|
||||
private BackupRepository backupRepo = null;
|
||||
private String commitName; // can be null
|
||||
|
||||
@Deprecated
|
||||
public SnapShooter(SolrCore core, String location, String snapshotName)
|
||||
{
|
||||
String snapDirStr;
|
||||
// Note - This logic is only applicable to the usecase where a shared file-system is exposed via
|
||||
// local file-system interface (primarily for backwards compatibility). For other use-cases, users
|
||||
// will be required to specify "location" where the backup should be stored.
|
||||
if (location == null)
|
||||
{
|
||||
snapDirStr = core.getDataDir();
|
||||
}
|
||||
else
|
||||
{
|
||||
snapDirStr = core.getCoreDescriptor().getInstanceDir().resolve(location).normalize().toString();
|
||||
}
|
||||
initialize(new LocalFileSystemRepository(), core, Paths.get(snapDirStr).toUri(), snapshotName, null);
|
||||
}
|
||||
|
||||
SnapShooter(BackupRepository backupRepo, SolrCore core, URI location, String snapshotName, String commitName)
|
||||
{
|
||||
initialize(backupRepo, core, location, snapshotName, commitName);
|
||||
}
|
||||
|
||||
private void initialize(BackupRepository backupRepo, SolrCore core, URI location, String snapshotName, String commitName)
|
||||
{
|
||||
this.solrCore = Objects.requireNonNull(core);
|
||||
this.backupRepo = Objects.requireNonNull(backupRepo);
|
||||
this.baseSnapDirPath = location;
|
||||
this.snapshotName = snapshotName;
|
||||
if (snapshotName != null)
|
||||
{
|
||||
directoryName = "snapshot." + snapshotName;
|
||||
}
|
||||
else
|
||||
{
|
||||
SimpleDateFormat fmt = new SimpleDateFormat(DATE_FMT, Locale.ROOT);
|
||||
directoryName = "snapshot." + fmt.format(new Date());
|
||||
}
|
||||
this.snapshotDirPath = backupRepo.resolve(location, directoryName);
|
||||
this.commitName = commitName;
|
||||
}
|
||||
|
||||
public BackupRepository getBackupRepository()
|
||||
{
|
||||
return backupRepo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent directory of the snapshots. This is the {@code location}
|
||||
* given in the constructor.
|
||||
*/
|
||||
public URI getLocation()
|
||||
{
|
||||
return this.baseSnapDirPath;
|
||||
}
|
||||
|
||||
void validateDeleteSnapshot()
|
||||
{
|
||||
Objects.requireNonNull(this.snapshotName);
|
||||
|
||||
boolean dirFound = false;
|
||||
String[] paths;
|
||||
try
|
||||
{
|
||||
paths = backupRepo.listAll(baseSnapDirPath);
|
||||
for (String path : paths)
|
||||
{
|
||||
if (path.equals(this.directoryName)
|
||||
&& backupRepo.getPathType(baseSnapDirPath.resolve(path)) == PathType.DIRECTORY)
|
||||
{
|
||||
dirFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dirFound)
|
||||
{
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
||||
"Snapshot " + snapshotName + " cannot be found in directory: " + baseSnapDirPath);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
|
||||
"Unable to find snapshot " + snapshotName + " in directory: " + baseSnapDirPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
void deleteSnapAsync(final AlfrescoReplicationHandler alfrescoReplicationHandler)
|
||||
{
|
||||
new Thread(() -> deleteNamedSnapshot(alfrescoReplicationHandler)).start();
|
||||
}
|
||||
|
||||
void validateCreateSnapshot() throws IOException
|
||||
{
|
||||
// Note - Removed the current behavior of creating the directory hierarchy.
|
||||
// Do we really need to provide this support?
|
||||
if (!backupRepo.exists(baseSnapDirPath))
|
||||
{
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
||||
" Directory does not exist: " + snapshotDirPath);
|
||||
}
|
||||
|
||||
if (backupRepo.exists(snapshotDirPath))
|
||||
{
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
||||
"Snapshot directory already exists: " + snapshotDirPath);
|
||||
}
|
||||
}
|
||||
|
||||
public NamedList createSnapshot() throws Exception
|
||||
{
|
||||
RefCounted<SolrIndexSearcher> searcher = solrCore.getSearcher();
|
||||
try
|
||||
{
|
||||
if (commitName != null)
|
||||
{
|
||||
SolrSnapshotMetaDataManager snapshotMgr = solrCore.getSnapshotMetaDataManager();
|
||||
Optional<IndexCommit> commit = snapshotMgr.getIndexCommitByName(commitName);
|
||||
if (commit.isPresent())
|
||||
{
|
||||
return createSnapshot(commit.get());
|
||||
}
|
||||
throw new SolrException(ErrorCode.SERVER_ERROR,
|
||||
"Unable to find an index commit with name " + commitName + " for core " + solrCore.getName());
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO should we try solrCore.getDeletionPolicy().getLatestCommit() first?
|
||||
IndexDeletionPolicyWrapper deletionPolicy = solrCore.getDeletionPolicy();
|
||||
IndexCommit indexCommit = searcher.get().getIndexReader().getIndexCommit();
|
||||
deletionPolicy.saveCommitPoint(indexCommit.getGeneration());
|
||||
try
|
||||
{
|
||||
return createSnapshot(indexCommit);
|
||||
}
|
||||
finally
|
||||
{
|
||||
deletionPolicy.releaseCommitPoint(indexCommit.getGeneration());
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
searcher.decref();
|
||||
}
|
||||
}
|
||||
|
||||
void createSnapAsync(final IndexCommit indexCommit, final int numberToKeep, Consumer<NamedList> result)
|
||||
{
|
||||
solrCore.getDeletionPolicy().saveCommitPoint(indexCommit.getGeneration());
|
||||
|
||||
new Thread(() -> {
|
||||
try
|
||||
{
|
||||
result.accept(createSnapshot(indexCommit));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.error("Exception while creating snapshot", e);
|
||||
NamedList snapShootDetails = new NamedList<>();
|
||||
snapShootDetails.add("snapShootException", e.getMessage());
|
||||
result.accept(snapShootDetails);
|
||||
}
|
||||
finally
|
||||
{
|
||||
solrCore.getDeletionPolicy().releaseCommitPoint(indexCommit.getGeneration());
|
||||
}
|
||||
if (snapshotName == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
deleteOldBackups(numberToKeep);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.warn("Unable to delete old snapshots ", e);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
|
||||
// note: remember to reserve the indexCommit first so it won't get deleted concurrently
|
||||
private NamedList createSnapshot(final IndexCommit indexCommit) throws Exception
|
||||
{
|
||||
LOG.info("Creating backup snapshot " + (snapshotName == null ? "<not named>" : snapshotName) + " at "
|
||||
+ baseSnapDirPath);
|
||||
boolean success = false;
|
||||
try
|
||||
{
|
||||
NamedList<Object> details = new NamedList<>();
|
||||
details.add("startTime", new Date().toString());//bad; should be Instant.now().toString()
|
||||
|
||||
Collection<String> files = indexCommit.getFileNames();
|
||||
Directory dir = solrCore.getDirectoryFactory()
|
||||
.get(solrCore.getIndexDir(), DirContext.DEFAULT, solrCore.getSolrConfig().indexConfig.lockType);
|
||||
try
|
||||
{
|
||||
for (String fileName : files)
|
||||
{
|
||||
backupRepo.copyFileFrom(dir, fileName, snapshotDirPath);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
solrCore.getDirectoryFactory().release(dir);
|
||||
}
|
||||
|
||||
details.add("fileCount", files.size());
|
||||
details.add("status", "success");
|
||||
details.add("snapshotCompletedAt", new Date().toString());//bad; should be Instant.now().toString()
|
||||
details.add("snapshotName", snapshotName);
|
||||
LOG.info("Done creating backup snapshot: " + (snapshotName == null ? "<not named>" : snapshotName) + " at "
|
||||
+ baseSnapDirPath);
|
||||
success = true;
|
||||
return details;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
try
|
||||
{
|
||||
backupRepo.deleteDirectory(snapshotDirPath);
|
||||
}
|
||||
catch (Exception excDuringDelete)
|
||||
{
|
||||
LOG.warn("Failed to delete " + snapshotDirPath + " after snapshot creation failed due to: "
|
||||
+ excDuringDelete);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteOldBackups(int numberToKeep) throws IOException
|
||||
{
|
||||
String[] paths = backupRepo.listAll(baseSnapDirPath);
|
||||
List<OldBackupDirectory> dirs = new ArrayList<>();
|
||||
for (String f : paths)
|
||||
{
|
||||
if (backupRepo.getPathType(baseSnapDirPath.resolve(f)) == PathType.DIRECTORY)
|
||||
{
|
||||
OldBackupDirectory obd = new OldBackupDirectory(baseSnapDirPath, f);
|
||||
if (obd.getTimestamp().isPresent())
|
||||
{
|
||||
dirs.add(obd);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (numberToKeep > dirs.size() - 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Collections.sort(dirs);
|
||||
int i = 1;
|
||||
for (OldBackupDirectory dir : dirs)
|
||||
{
|
||||
if (i++ > numberToKeep)
|
||||
{
|
||||
backupRepo.deleteDirectory(dir.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void deleteNamedSnapshot(AlfrescoReplicationHandler alfrescoReplicationHandler)
|
||||
{
|
||||
LOG.info("Deleting snapshot: " + snapshotName);
|
||||
|
||||
NamedList<Object> details = new NamedList<>();
|
||||
|
||||
try
|
||||
{
|
||||
URI path = baseSnapDirPath.resolve("snapshot." + snapshotName);
|
||||
backupRepo.deleteDirectory(path);
|
||||
|
||||
details.add("status", "success");
|
||||
details.add("snapshotDeletedAt", new Date().toString());
|
||||
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
details.add("status", "Unable to delete snapshot: " + snapshotName);
|
||||
LOG.warn("Unable to delete snapshot: " + snapshotName, e);
|
||||
}
|
||||
|
||||
alfrescoReplicationHandler.snapShootDetails = details;
|
||||
}
|
||||
|
||||
private static final String DATE_FMT = "yyyyMMddHHmmssSSS";
|
||||
|
||||
}
|
||||
+5
-4
@@ -35,7 +35,7 @@ import org.alfresco.solr.tracker.CommitTracker;
|
||||
import org.alfresco.solr.tracker.ContentTracker;
|
||||
import org.alfresco.solr.tracker.MetadataTracker;
|
||||
import org.alfresco.solr.tracker.ModelTracker;
|
||||
import org.alfresco.solr.tracker.SlaveNodeStatePublisher;
|
||||
import org.alfresco.solr.tracker.SlaveCoreStatePublisher;
|
||||
import org.alfresco.solr.tracker.SolrTrackerScheduler;
|
||||
import org.alfresco.solr.tracker.Tracker;
|
||||
import org.alfresco.solr.tracker.TrackerRegistry;
|
||||
@@ -113,7 +113,7 @@ public class SolrCoreLoadListener extends AbstractSolrEventListener
|
||||
AlfrescoSolrDataModel.getInstance().getDictionaryService(CMISStrictDictionaryService.DEFAULT),
|
||||
AlfrescoSolrDataModel.getInstance().getNamespaceDAO());
|
||||
|
||||
SolrContentStore contentStore = new SolrContentStore(coreContainer.getSolrHome());
|
||||
SolrContentStore contentStore = admin.getSolrContentStore();
|
||||
SolrInformationServer informationServer = new SolrInformationServer(admin, core, repositoryClient, contentStore);
|
||||
coreProperties.putAll(informationServer.getProps());
|
||||
admin.getInformationServers().put(core.getName(), informationServer);
|
||||
@@ -162,6 +162,7 @@ public class SolrCoreLoadListener extends AbstractSolrEventListener
|
||||
|
||||
boolean trackersHaveBeenEnabled = Boolean.parseBoolean(coreProperties.getProperty("enable.alfresco.tracking", "true"));
|
||||
boolean owningCoreIsSlave = isSlaveModeEnabledFor(core);
|
||||
contentStore.toggleReadOnlyMode(owningCoreIsSlave);
|
||||
|
||||
if (trackerRegistry.hasTrackersForCore(core.getName()))
|
||||
{
|
||||
@@ -182,7 +183,7 @@ public class SolrCoreLoadListener extends AbstractSolrEventListener
|
||||
{
|
||||
LOGGER.info("SearchServices Core Trackers have been explicitly disabled on core \"{}\" through \"enable.alfresco.tracking\" configuration property.", core.getName());
|
||||
|
||||
SlaveNodeStatePublisher statePublisher = new SlaveNodeStatePublisher(false, coreProperties, repositoryClient, core.getName(), informationServer);
|
||||
SlaveCoreStatePublisher statePublisher = new SlaveCoreStatePublisher(false, coreProperties, repositoryClient, core.getName(), informationServer);
|
||||
trackerRegistry.register(core.getName(), statePublisher);
|
||||
scheduler.schedule(statePublisher, core.getName(), coreProperties);
|
||||
trackers.add(statePublisher);
|
||||
@@ -197,7 +198,7 @@ public class SolrCoreLoadListener extends AbstractSolrEventListener
|
||||
{
|
||||
LOGGER.info("SearchServices Core Trackers have been disabled on core \"{}\" because it is a slave core.", core.getName());
|
||||
|
||||
SlaveNodeStatePublisher statePublisher = new SlaveNodeStatePublisher(false, coreProperties, repositoryClient, core.getName(), informationServer);
|
||||
SlaveCoreStatePublisher statePublisher = new SlaveCoreStatePublisher(false, coreProperties, repositoryClient, core.getName(), informationServer);
|
||||
trackerRegistry.register(core.getName(), statePublisher);
|
||||
scheduler.schedule(statePublisher, core.getName(), coreProperties);
|
||||
trackers.add(statePublisher);
|
||||
|
||||
+2
@@ -151,6 +151,8 @@ public class CommitTracker extends AbstractTracker
|
||||
maintenance();
|
||||
}
|
||||
|
||||
infoSrv.flushContentStore();
|
||||
|
||||
//Do the commit opening the searcher if needed. This will commit all the work done by indexing trackers.
|
||||
//This will return immediately and not wait for searchers to warm
|
||||
//System.out.println("################### Commit:"+openSearcherNeeded);
|
||||
|
||||
+30
-3
@@ -41,6 +41,7 @@ import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.solr.AlfrescoCoreAdminHandler;
|
||||
import org.alfresco.solr.AlfrescoSolrDataModel;
|
||||
import org.alfresco.solr.InformationServer;
|
||||
import org.alfresco.solr.NodeReport;
|
||||
import org.alfresco.solr.TrackerState;
|
||||
import org.alfresco.solr.client.SOLRAPIClient;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -59,7 +60,7 @@ import java.util.Properties;
|
||||
* @since 1.5
|
||||
* @see <a href="https://issues.alfresco.com/jira/browse/SEARCH-1752">SEARCH-1752</a>
|
||||
*/
|
||||
public abstract class NodeStatePublisher extends AbstractTracker
|
||||
public abstract class CoreStatePublisher extends AbstractTracker
|
||||
{
|
||||
DocRouter docRouter;
|
||||
private final boolean isMaster;
|
||||
@@ -70,7 +71,7 @@ public abstract class NodeStatePublisher extends AbstractTracker
|
||||
/** The property to use for determining the shard. */
|
||||
protected Optional<QName> shardProperty = Optional.empty();
|
||||
|
||||
NodeStatePublisher(
|
||||
CoreStatePublisher(
|
||||
boolean isMaster,
|
||||
Properties p,
|
||||
SOLRAPIClient client,
|
||||
@@ -88,12 +89,28 @@ public abstract class NodeStatePublisher extends AbstractTracker
|
||||
docRouter = DocRouterFactory.getRouter(p, ShardMethodEnum.getShardMethod(shardMethod));
|
||||
}
|
||||
|
||||
NodeStatePublisher(Type type)
|
||||
CoreStatePublisher(Type type)
|
||||
{
|
||||
super(type);
|
||||
this.isMaster = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about the {@link org.alfresco.solr.client.Node} associated with the given dbid.
|
||||
*
|
||||
* @param dbid the node identifier.
|
||||
* @return the {@link org.alfresco.solr.client.Node} associated with the given dbid.
|
||||
*/
|
||||
public NodeReport checkNode(Long dbid)
|
||||
{
|
||||
NodeReport nodeReport = new NodeReport();
|
||||
nodeReport.setDbid(dbid);
|
||||
|
||||
this.infoSrv.addCommonNodeReportInfo(nodeReport);
|
||||
|
||||
return nodeReport;
|
||||
}
|
||||
|
||||
private void firstUpdateShardProperty()
|
||||
{
|
||||
shardKey.ifPresent( shardKeyName -> {
|
||||
@@ -222,4 +239,14 @@ public abstract class NodeStatePublisher extends AbstractTracker
|
||||
{
|
||||
return this.docRouter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the hosting core is master or standalone.
|
||||
*
|
||||
* @return true if the hosting core is master or standalone.
|
||||
*/
|
||||
public boolean isOnMasterOrStandalone()
|
||||
{
|
||||
return isMaster;
|
||||
}
|
||||
}
|
||||
+9
-25
@@ -49,7 +49,7 @@ import org.slf4j.LoggerFactory;
|
||||
* This tracks two things: transactions and metadata nodes
|
||||
* @author Ahmed Owian
|
||||
*/
|
||||
public class MetadataTracker extends NodeStatePublisher implements Tracker
|
||||
public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
{
|
||||
protected final static Logger log = LoggerFactory.getLogger(MetadataTracker.class);
|
||||
private static final int DEFAULT_TRANSACTION_DOCS_BATCH_SIZE = 100;
|
||||
@@ -888,24 +888,18 @@ public class MetadataTracker extends NodeStatePublisher implements Tracker
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public NodeReport checkNode(Long dbid)
|
||||
{
|
||||
NodeReport nodeReport = new NodeReport();
|
||||
nodeReport.setDbid(dbid);
|
||||
NodeReport nodeReport = super.checkNode(dbid);
|
||||
|
||||
// In DB
|
||||
|
||||
GetNodesParameters parameters = new GetNodesParameters();
|
||||
parameters.setFromNodeId(dbid);
|
||||
parameters.setToNodeId(dbid);
|
||||
List<Node> dbnodes;
|
||||
try
|
||||
{
|
||||
dbnodes = client.getNodes(parameters, 1);
|
||||
List<Node> dbnodes = client.getNodes(parameters, 1);
|
||||
if (dbnodes.size() == 1)
|
||||
{
|
||||
Node dbnode = dbnodes.get(0);
|
||||
@@ -915,41 +909,31 @@ public class MetadataTracker extends NodeStatePublisher implements Tracker
|
||||
else
|
||||
{
|
||||
nodeReport.setDbNodeStatus(SolrApiNodeStatus.UNKNOWN);
|
||||
nodeReport.setDbTx(-1l);
|
||||
nodeReport.setDbTx(-1L);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
nodeReport.setDbNodeStatus(SolrApiNodeStatus.UNKNOWN);
|
||||
nodeReport.setDbTx(-2l);
|
||||
nodeReport.setDbTx(-2L);
|
||||
}
|
||||
catch (JSONException e)
|
||||
{
|
||||
nodeReport.setDbNodeStatus(SolrApiNodeStatus.UNKNOWN);
|
||||
nodeReport.setDbTx(-3l);
|
||||
nodeReport.setDbTx(-3L);
|
||||
}
|
||||
catch (AuthenticationException e1)
|
||||
{
|
||||
nodeReport.setDbNodeStatus(SolrApiNodeStatus.UNKNOWN);
|
||||
nodeReport.setDbTx(-4l);
|
||||
nodeReport.setDbTx(-4L);
|
||||
}
|
||||
|
||||
this.infoSrv.addCommonNodeReportInfo(nodeReport);
|
||||
|
||||
return nodeReport;
|
||||
}
|
||||
|
||||
public NodeReport checkNode(Node node)
|
||||
{
|
||||
NodeReport nodeReport = new NodeReport();
|
||||
nodeReport.setDbid(node.getId());
|
||||
|
||||
nodeReport.setDbNodeStatus(node.getStatus());
|
||||
nodeReport.setDbTx(node.getTxnId());
|
||||
|
||||
this.infoSrv.addCommonNodeReportInfo(nodeReport);
|
||||
|
||||
return nodeReport;
|
||||
return checkNode(node.getId());
|
||||
}
|
||||
|
||||
public List<Node> getFullNodesForDbTransaction(Long txid)
|
||||
|
||||
+10
-4
@@ -14,8 +14,8 @@ import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Despite belonging to the Tracker ecosystem, this component is actually a publisher, which periodically informs
|
||||
* Alfresco about the state of the hosting slave node.
|
||||
* As the name suggests, this worker is scheduled only when the hosting node acts as a slave.
|
||||
* Alfresco about the state of the hosting slave core.
|
||||
* As the name suggests, this worker is scheduled only when the owning core acts as a slave.
|
||||
* It allows Solr's master/slave setup to be used with dynamic shard registration.
|
||||
*
|
||||
* In this scenario the slave is polling a "tracking" Solr node. The tracker below calls
|
||||
@@ -27,9 +27,9 @@ import java.util.Properties;
|
||||
* @author Andrea Gazzarini
|
||||
* @since 1.5
|
||||
*/
|
||||
public class SlaveNodeStatePublisher extends NodeStatePublisher
|
||||
public class SlaveCoreStatePublisher extends CoreStatePublisher
|
||||
{
|
||||
public SlaveNodeStatePublisher(
|
||||
public SlaveCoreStatePublisher(
|
||||
boolean isMaster,
|
||||
Properties coreProperties,
|
||||
SOLRAPIClient repositoryClient,
|
||||
@@ -61,6 +61,12 @@ public class SlaveNodeStatePublisher extends NodeStatePublisher
|
||||
// Do nothing here
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnMasterOrStandalone()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMaintenance()
|
||||
{
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2019 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.solr.utils;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* @author Elia Porciani
|
||||
*/
|
||||
public class AlfrescoFileUtils {
|
||||
|
||||
/**
|
||||
* Check if two directories contains the same files
|
||||
*
|
||||
* @param dir
|
||||
* @param dir2
|
||||
* @param extensions Limits the search to the extensions provided
|
||||
* @param recursive Check recursively in all subdirs
|
||||
* @return
|
||||
*/
|
||||
public static boolean areDirectoryEquals(Path dir, Path dir2, String[] extensions, boolean recursive) {
|
||||
|
||||
Map<String, File> filesDir1 = FileUtils.listFiles(new File(dir.toUri()), extensions, recursive)
|
||||
.stream().collect(Collectors.toMap(f -> f.getName(), f -> f));
|
||||
Map<String, File> filesDir2 = FileUtils.listFiles(new File(dir2.toUri()), extensions, recursive)
|
||||
.stream().collect(Collectors.toMap(f -> f.getName(), f -> f));
|
||||
|
||||
if (filesDir1.size() != filesDir2.size())
|
||||
return false;
|
||||
|
||||
return filesDir1.entrySet().stream().allMatch(e -> {
|
||||
File fileDir2 = filesDir2.get(e.getKey());
|
||||
if (fileDir2 == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
byte[] otherBytes = Files.readAllBytes(e.getValue().toPath());
|
||||
byte[] thisBytes = Files.readAllBytes(fileDir2.toPath());
|
||||
|
||||
return (Arrays.equals(otherBytes, thisBytes));
|
||||
} catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2019 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.solr.utils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
public abstract class Utils
|
||||
{
|
||||
/**
|
||||
* Returns the same input collection if that is not null, otherwise a new empty collection.
|
||||
* Provides a safe way for iterating over a returned collection (which could be null).
|
||||
*
|
||||
* @param values the collection.
|
||||
* @param <T> the collection type.
|
||||
* @return the same input collection if that is not null, otherwise a new empty collection.
|
||||
*/
|
||||
public static <T> Collection<T> notNullOrEmpty(Collection<T> values)
|
||||
{
|
||||
return values != null ? values : Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given input in an Integer, otherwise it returns null.
|
||||
*
|
||||
* @param value the numeric string.
|
||||
* @return the corresponding Integer or null in case the input is NaN.
|
||||
*/
|
||||
public static Integer toIntOrNull(String value)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Integer.valueOf(value);
|
||||
}
|
||||
catch(NumberFormatException nfe)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -1154,7 +1154,9 @@
|
||||
|
||||
https://wiki.apache.org/solr/SolrCloud/
|
||||
-->
|
||||
<requestHandler name="/replication" class="solr.ReplicationHandler" >
|
||||
|
||||
<requestHandler name="/replication" class="org.alfresco.solr.handler.AlfrescoReplicationHandler">
|
||||
|
||||
<!--
|
||||
To enable simple master/slave replication, uncomment one of the
|
||||
sections below, depending on whether this solr instance should be
|
||||
|
||||
+2
-3
@@ -87,7 +87,7 @@ import static org.alfresco.repo.search.adaptor.lucene.QueryConstants.FIELD_VERSI
|
||||
* @author Michael Suzuki
|
||||
*/
|
||||
@ThreadLeakLingering(linger = 5000)
|
||||
public abstract class AbstractAlfrescoDistributedTest extends SolrTestInitializer
|
||||
public abstract class AbstractAlfrescoDistributedIT extends SolrITInitializer
|
||||
{
|
||||
protected static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
@@ -296,6 +296,7 @@ public abstract class AbstractAlfrescoDistributedTest extends SolrTestInitialize
|
||||
{
|
||||
long begin = System.currentTimeMillis();
|
||||
//TODO: Support multiple cores per jetty
|
||||
System.out.println(getStandaloneClients().size());
|
||||
SolrClient standaloneClient = getStandaloneClients().get(0); //Get the first one
|
||||
waitForDocCountCore(standaloneClient, query, count, waitMillis, begin);
|
||||
waitForShardsCount(query, count, waitMillis, begin);
|
||||
@@ -760,8 +761,6 @@ public abstract class AbstractAlfrescoDistributedTest extends SolrTestInitialize
|
||||
params.remove("distrib");
|
||||
setDistributedParams(params);
|
||||
QueryResponse rsp = queryRandomShard(json, params);
|
||||
System.out.println("Cluster Response:"+rsp);
|
||||
System.out.println("Control Response:"+controlRsp);
|
||||
solrComparator.compareResponses(rsp, controlRsp);
|
||||
return rsp;
|
||||
}
|
||||
+48
-13
@@ -27,13 +27,13 @@ import org.alfresco.solr.client.NodeMetaData;
|
||||
import org.alfresco.solr.client.SOLRAPIQueueClient;
|
||||
import org.alfresco.solr.client.Transaction;
|
||||
import org.alfresco.solr.tracker.Tracker;
|
||||
import org.alfresco.util.SearchLanguageConversion;
|
||||
import org.apache.chemistry.opencmis.commons.impl.json.JSONArray;
|
||||
import org.apache.chemistry.opencmis.commons.impl.json.JSONObject;
|
||||
import org.apache.chemistry.opencmis.commons.impl.json.JSONValue;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.util.Time;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
@@ -67,6 +67,7 @@ import org.xml.sax.SAXException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.file.Paths;
|
||||
@@ -84,19 +85,25 @@ import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Base class that provides the solr test harness.
|
||||
* This is used to manage the embedded solr used for unit and integration testing.
|
||||
* This is used to manage the embedded solr used for integration testing.
|
||||
* The abstract also provides helper method that interacts with the
|
||||
* embedded solr.
|
||||
*
|
||||
* @author Michael Suzuki
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractAlfrescoSolrTests implements SolrTestFiles, AlfrescoSolrConstants
|
||||
public abstract class AbstractAlfrescoSolrIT implements SolrTestFiles, AlfrescoSolrConstants
|
||||
{
|
||||
static AlfrescoCoreAdminHandler admin;
|
||||
private static Log LOG = LogFactory.getLog(AbstractAlfrescoSolrTests.class);
|
||||
private static Log LOG = LogFactory.getLog(AbstractAlfrescoSolrIT.class);
|
||||
private static boolean CORE_NOT_YET_CREATED = true;
|
||||
|
||||
|
||||
private static String testExecutionSolrHome;
|
||||
private static String testSolrCollection;
|
||||
private static String testSolrConf;
|
||||
private static String templateConf;
|
||||
|
||||
/**
|
||||
* Harness initialized by initTestHarness.
|
||||
* <p/>
|
||||
@@ -119,6 +126,20 @@ public abstract class AbstractAlfrescoSolrTests implements SolrTestFiles, Alfres
|
||||
protected static NodeRef TEST_NODEREF;
|
||||
protected static Date FTS_TEST_DATE;
|
||||
|
||||
|
||||
|
||||
protected static void copyTestFiles() throws IOException {
|
||||
|
||||
//Add solr home conf folder with alfresco based configuration.
|
||||
|
||||
File testExecutionFolder = Paths.get(testExecutionSolrHome).toFile();
|
||||
|
||||
for (String s : List.of("/conf", "/alfrescoModels", "/templates", "/collection1")) {
|
||||
FileUtils.copyDirectory(Paths.get(TEST_FILES_LOCATION + s).toFile(), Paths.get(testExecutionFolder + s).toFile());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Bunch of methods that wrap testHarness object usage.
|
||||
* TestHarness is a class for internal solr test and should not be use outside Solr.
|
||||
* Unfortunately this class is used in too many test and a complete refactor would have a huge impact
|
||||
@@ -218,6 +239,7 @@ public abstract class AbstractAlfrescoSolrTests implements SolrTestFiles, Alfres
|
||||
@Deprecated
|
||||
public static void initAlfrescoCore(String schema) throws Exception
|
||||
{
|
||||
|
||||
LOG.info("##################################### init Alfresco core ##############");
|
||||
LOG.info("####initCore");
|
||||
System.setProperty("solr.solr.home", TEST_FILES_LOCATION);
|
||||
@@ -231,18 +253,29 @@ public abstract class AbstractAlfrescoSolrTests implements SolrTestFiles, Alfres
|
||||
System.setProperty("alfresco.test", "true");
|
||||
System.setProperty("solr.tests.mergeScheduler", "org.apache.lucene.index.ConcurrentMergeScheduler");
|
||||
System.setProperty("solr.tests.mergePolicy", "org.apache.lucene.index.TieredMergePolicy");
|
||||
|
||||
if (CORE_NOT_YET_CREATED)
|
||||
{
|
||||
|
||||
testExecutionSolrHome = TEST_EXECUTION_FOLDER + "/" + Time.now() + "/solrhome";
|
||||
testSolrCollection = testExecutionSolrHome + "/collection1";
|
||||
testSolrConf = testSolrCollection + "/conf/";
|
||||
templateConf = testExecutionSolrHome + "/templates/%s/conf/";
|
||||
|
||||
copyTestFiles();
|
||||
createAlfrescoCore(schema);
|
||||
}
|
||||
LOG.info("####initCore end");
|
||||
|
||||
|
||||
|
||||
admin = of(h).map(TestHarness::getCoreContainer)
|
||||
.map(CoreContainer::getMultiCoreHandler)
|
||||
.map(AlfrescoCoreAdminHandler.class::cast)
|
||||
.orElseThrow(RuntimeException::new);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated as testHarness is used
|
||||
*/
|
||||
@@ -263,11 +296,11 @@ public abstract class AbstractAlfrescoSolrTests implements SolrTestFiles, Alfres
|
||||
{
|
||||
String templateName = System.getProperty("templateName", "rerank");
|
||||
FileUtils.copyFile(
|
||||
Paths.get(String.format(TEMPLATE_CONF, templateName) + schema).toFile(),
|
||||
Paths.get(TEST_SOLR_CONF + schema).toFile());
|
||||
Paths.get(String.format(templateConf, templateName) + schema).toFile(),
|
||||
Paths.get(testSolrConf + schema).toFile());
|
||||
}
|
||||
|
||||
SolrResourceLoader resourceLoader = new SolrResourceLoader(Paths.get(TEST_FILES_LOCATION), null, properties);
|
||||
SolrResourceLoader resourceLoader = new SolrResourceLoader(Paths.get(testExecutionSolrHome), null, properties);
|
||||
TestCoresLocator locator = new TestCoresLocator(SolrTestCaseJ4.DEFAULT_TEST_CORENAME,
|
||||
"data",
|
||||
"solrconfig.xml",
|
||||
@@ -292,9 +325,9 @@ public abstract class AbstractAlfrescoSolrTests implements SolrTestFiles, Alfres
|
||||
}
|
||||
|
||||
@AfterClass()
|
||||
public static void tearDown()
|
||||
{
|
||||
public static void tearDown() throws IOException {
|
||||
h.close();
|
||||
FileUtils.forceDelete(Paths.get(TEST_EXECUTION_FOLDER).toFile());
|
||||
CORE_NOT_YET_CREATED = true;
|
||||
}
|
||||
/**
|
||||
@@ -309,7 +342,7 @@ public abstract class AbstractAlfrescoSolrTests implements SolrTestFiles, Alfres
|
||||
/**
|
||||
* Validates an update XML String is successful
|
||||
*/
|
||||
public void assertU(String update)
|
||||
public void assertU(String update)
|
||||
{
|
||||
assertU(null, update);
|
||||
}
|
||||
@@ -325,7 +358,7 @@ public abstract class AbstractAlfrescoSolrTests implements SolrTestFiles, Alfres
|
||||
/**
|
||||
* Validates an update XML String failed
|
||||
*/
|
||||
public void assertFailedU(String update)
|
||||
public void assertFailedU(String update)
|
||||
{
|
||||
assertFailedU(null, update);
|
||||
}
|
||||
@@ -434,6 +467,7 @@ public abstract class AbstractAlfrescoSolrTests implements SolrTestFiles, Alfres
|
||||
}
|
||||
catch (Exception e2)
|
||||
{
|
||||
e2.printStackTrace();
|
||||
throw new RuntimeException("Exception during query", e2);
|
||||
}
|
||||
}
|
||||
@@ -498,7 +532,7 @@ public abstract class AbstractAlfrescoSolrTests implements SolrTestFiles, Alfres
|
||||
throws Exception
|
||||
{
|
||||
Date date = new Date();
|
||||
long timeout = (long)date.getTime() + waitMillis;
|
||||
long timeout = date.getTime() + waitMillis;
|
||||
|
||||
RefCounted<SolrIndexSearcher> ref = null;
|
||||
int totalHits = 0;
|
||||
@@ -738,6 +772,7 @@ public abstract class AbstractAlfrescoSolrTests implements SolrTestFiles, Alfres
|
||||
}
|
||||
catch(Exception exception)
|
||||
{
|
||||
exception.printStackTrace();
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
finally
|
||||
@@ -785,7 +820,7 @@ public abstract class AbstractAlfrescoSolrTests implements SolrTestFiles, Alfres
|
||||
{
|
||||
public SolrServletRequest(SolrCore core, HttpServletRequest req)
|
||||
{
|
||||
super(core, new MultiMapSolrParams(Collections.<String, String[]> emptyMap()));
|
||||
super(core, new MultiMapSolrParams(Collections.emptyMap()));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -43,7 +43,7 @@ import static org.alfresco.solr.AlfrescoSolrUtils.*;
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class AdminHandlerDistributedTest extends AbstractAlfrescoDistributedTest
|
||||
public class AdminHandlerDistributedIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
final String JETTY_SERVER_ID = this.getClass().getSimpleName();
|
||||
@@ -52,7 +52,7 @@ public class AdminHandlerDistributedTest extends AbstractAlfrescoDistributedTest
|
||||
@BeforeClass
|
||||
private static void initData() throws Throwable
|
||||
{
|
||||
initSolrServers(2, "AdminHandlerDistributedTest", null);
|
||||
initSolrServers(2, "AdminHandlerDistributedIT", null);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
+2
-1
@@ -11,7 +11,8 @@ import org.junit.Test;
|
||||
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
public class AdminHandlerTest extends AbstractAlfrescoSolrTests {
|
||||
public class AdminHandlerIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
|
||||
static CoreAdminHandler admin;
|
||||
|
||||
+179
-10
@@ -20,15 +20,23 @@ package org.alfresco.solr;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.alfresco.solr.AlfrescoCoreAdminHandler.ALFRESCO_CORE_NAME;
|
||||
import static org.alfresco.solr.AlfrescoCoreAdminHandler.ARCHIVE_CORE_NAME;
|
||||
import static org.alfresco.solr.AlfrescoCoreAdminHandler.ARG_TXID;
|
||||
import static org.alfresco.solr.AlfrescoCoreAdminHandler.STORE_REF_MAP;
|
||||
import static org.alfresco.solr.AlfrescoCoreAdminHandler.VERSION_CORE_NAME;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -42,13 +50,18 @@ import java.util.stream.Collectors;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.solr.adapters.IOpenBitSet;
|
||||
import org.alfresco.solr.tracker.AclTracker;
|
||||
import org.alfresco.solr.tracker.DocRouter;
|
||||
import org.alfresco.solr.tracker.IndexHealthReport;
|
||||
import org.alfresco.solr.tracker.MetadataTracker;
|
||||
import org.alfresco.solr.tracker.PropertyRouter;
|
||||
import org.alfresco.solr.tracker.SlaveCoreStatePublisher;
|
||||
import org.alfresco.solr.tracker.TrackerRegistry;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.common.params.CoreAdminParams;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.apache.solr.common.params.SolrParams;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.response.SolrQueryResponse;
|
||||
import org.junit.Before;
|
||||
@@ -60,7 +73,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
/** Unit tests for {@link org.alfresco.solr.AlfrescoCoreAdminHandler}. */
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AlfrescoCoreAdminHandlerTest
|
||||
public class AlfrescoCoreAdminHandlerIT
|
||||
{
|
||||
/** The string representing a transaction report. */
|
||||
private static final String TXREPORT = "TXREPORT";
|
||||
@@ -105,6 +118,169 @@ public class AlfrescoCoreAdminHandlerTest
|
||||
when(req.getParams()).thenReturn(params);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractShardsWithEmptyParameter_shouldReturnAnEmptyList()
|
||||
{
|
||||
assertTrue(alfrescoCoreAdminHandler.extractShards("", Integer.MAX_VALUE).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractShardsWithNullParameter_shouldReturnAnEmptyList()
|
||||
{
|
||||
assertTrue(alfrescoCoreAdminHandler.extractShards(null, Integer.MAX_VALUE).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractShardsWithOneInvalidShard_shouldReturnAnEmptyList()
|
||||
{
|
||||
assertTrue(alfrescoCoreAdminHandler.extractShards("This is an invalid shard id", Integer.MAX_VALUE).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractShardsWithOneShards_shouldReturnSingletonList()
|
||||
{
|
||||
assertEquals(singletonList(1), alfrescoCoreAdminHandler.extractShards("1", Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractShardsWithSeveralValidShards_shouldReturnAllOfThemInTheList()
|
||||
{
|
||||
assertEquals(asList(1,5,6,11,23), alfrescoCoreAdminHandler.extractShards("1,5,6,11,23", Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractShardsWithSeveralValidShards_shouldReturnOnlyValidIdentifiers()
|
||||
{
|
||||
assertEquals(asList(1,5,6,11,23), alfrescoCoreAdminHandler.extractShards("1,5,A,6,xyz,11,BB,23,o01z", Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractShardsWithSeveralValidShardsAndLimit_shouldConsiderOnlyShardsLesserThanLimit()
|
||||
{
|
||||
assertEquals(asList(1,5,6,11,12), alfrescoCoreAdminHandler.extractShards("1,5,6,11,23,25,99,223,12", 23));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAlfrescoCoreWhenInputIsNull_shouldReturnFalse()
|
||||
{
|
||||
assertFalse(alfrescoCoreAdminHandler.hasAlfrescoCore(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAlfrescoCoreWhenWeHaveNoCore_shouldReturnFalse()
|
||||
{
|
||||
assertFalse(alfrescoCoreAdminHandler.hasAlfrescoCore(emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAlfrescoCoreWhenDoesntHaveAnyTracker_shouldReturnFalse()
|
||||
{
|
||||
assertFalse(alfrescoCoreAdminHandler.hasAlfrescoCore(emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAlfrescoCoreWithRegisteredTrackers_shouldReturnTrue()
|
||||
{
|
||||
when(trackerRegistry.hasTrackersForCore("CoreD")).thenReturn(true);
|
||||
assertTrue(alfrescoCoreAdminHandler.hasAlfrescoCore(asList(dummyCore("CoreA"), dummyCore("CoreB"), dummyCore("CoreC"), dummyCore("CoreD"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void trackerRegistryHasNoCoreNames_itShouldReturnAnEmptyList()
|
||||
{
|
||||
assertTrue(alfrescoCoreAdminHandler.coreNames().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void coreDetectedAsMasterOrStandalone()
|
||||
{
|
||||
MetadataTracker coreStatePublisher = mock(MetadataTracker.class);
|
||||
|
||||
when(trackerRegistry.getTrackerForCore(anyString(), eq(MetadataTracker.class)))
|
||||
.thenReturn(coreStatePublisher);
|
||||
|
||||
assertTrue(alfrescoCoreAdminHandler.isMasterOrStandalone("ThisIsTheCoreName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void coreDetectedAsSlave()
|
||||
{
|
||||
when(trackerRegistry.getTrackerForCore(anyString(), eq(MetadataTracker.class))).thenReturn(null);
|
||||
assertFalse(alfrescoCoreAdminHandler.isMasterOrStandalone("ThisIsTheCoreName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void coreIsMaster_thenCoreStatePublisherInstanceCorrespondsToMetadataTracker()
|
||||
{
|
||||
MetadataTracker coreStatePublisher = mock(MetadataTracker.class);
|
||||
|
||||
when(trackerRegistry.getTrackerForCore(anyString(), eq(MetadataTracker.class)))
|
||||
.thenReturn(coreStatePublisher);
|
||||
|
||||
assertSame(coreStatePublisher, alfrescoCoreAdminHandler.coreStatePublisher("ThisIsTheCoreName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void coreIsSlave_thenCoreStatePublisherInstanceCorrespondsToSlaveCoreStatePublisher()
|
||||
{
|
||||
SlaveCoreStatePublisher coreStatePublisher = mock(SlaveCoreStatePublisher.class);
|
||||
|
||||
when(trackerRegistry.getTrackerForCore(anyString(), eq(MetadataTracker.class))).thenReturn(null);
|
||||
when(trackerRegistry.getTrackerForCore(anyString(), eq(SlaveCoreStatePublisher.class))).thenReturn(coreStatePublisher);
|
||||
|
||||
assertSame(coreStatePublisher, alfrescoCoreAdminHandler.coreStatePublisher("ThisIsTheCoreName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void coreIsSlave_thenDocRouterIsNull()
|
||||
{
|
||||
String coreName = "aCore";
|
||||
when(trackerRegistry.getTrackerForCore(eq(coreName), eq(MetadataTracker.class))).thenReturn(null);
|
||||
assertNull(alfrescoCoreAdminHandler.getDocRouter("aCore"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void coreIsMaster_thenDocRouterIsProperlyReturned()
|
||||
{
|
||||
DocRouter expectedRouter = new PropertyRouter("someProperty_.{1,35}");
|
||||
|
||||
MetadataTracker coreStatePublisher = mock(MetadataTracker.class);
|
||||
when(coreStatePublisher.getDocRouter()).thenReturn(expectedRouter);
|
||||
when(trackerRegistry.getTrackerForCore(anyString(), eq(MetadataTracker.class))).thenReturn(coreStatePublisher);
|
||||
|
||||
assertSame(expectedRouter, alfrescoCoreAdminHandler.getDocRouter("aCore"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void targetCoreNameCanBeSpecifiedInSeveralWays()
|
||||
{
|
||||
String coreName = "ThisIsTheCoreName";
|
||||
|
||||
ModifiableSolrParams params = new ModifiableSolrParams();
|
||||
|
||||
assertNull(alfrescoCoreAdminHandler.coreName(params));
|
||||
|
||||
params.set(CoreAdminParams.CORE, coreName);
|
||||
|
||||
assertEquals(coreName, alfrescoCoreAdminHandler.coreName(params));
|
||||
|
||||
params.remove(CoreAdminParams.CORE);
|
||||
assertNull(alfrescoCoreAdminHandler.coreName(params));
|
||||
|
||||
params.set("coreName", coreName);
|
||||
|
||||
assertEquals(coreName, alfrescoCoreAdminHandler.coreName(params));
|
||||
assertEquals(coreName, alfrescoCoreAdminHandler.coreName(params));
|
||||
}
|
||||
|
||||
|
||||
private SolrCore dummyCore(String name)
|
||||
{
|
||||
SolrCore core = mock(SolrCore.class);
|
||||
when(core.getName()).thenReturn(name);
|
||||
return core;
|
||||
}
|
||||
|
||||
/** Check that a transaction report can be generated. */
|
||||
@Test
|
||||
public void handleCustomActionTXReportSuccess() throws Exception
|
||||
@@ -143,8 +319,6 @@ public class AlfrescoCoreAdminHandlerTest
|
||||
public void handleCustomActionTXReportMissingTXId()
|
||||
{
|
||||
when(params.get(CoreAdminParams.ACTION)).thenReturn(TXREPORT);
|
||||
when(params.get(ARG_TXID)).thenReturn(null);
|
||||
|
||||
alfrescoCoreAdminHandler.handleCustomAction(req, rsp);
|
||||
|
||||
verify(rsp, never()).add(anyString(), any());
|
||||
@@ -156,11 +330,8 @@ public class AlfrescoCoreAdminHandlerTest
|
||||
{
|
||||
when(params.get(CoreAdminParams.ACTION)).thenReturn(TXREPORT);
|
||||
when(params.get(CoreAdminParams.CORE)).thenReturn(null);
|
||||
when(params.get(ARG_TXID)).thenReturn(TX_ID);
|
||||
|
||||
alfrescoCoreAdminHandler.handleCustomAction(req, rsp);
|
||||
|
||||
verify(rsp, never()).add(anyString(), any());
|
||||
}
|
||||
|
||||
/** Check that when an unknown action is provided we don't generate a report. */
|
||||
@@ -212,10 +383,9 @@ public class AlfrescoCoreAdminHandlerTest
|
||||
public void coreNamesAreTrimmed_oneCoreNameAtTime() {
|
||||
AlfrescoCoreAdminHandler spy = spy(new AlfrescoCoreAdminHandler() {
|
||||
@Override
|
||||
protected boolean newCore(String coreName, int numShards, StoreRef storeRef, String templateName, int replicationFactor, int nodeInstance, int numNodes, String shardIds, Properties extraProperties, SolrQueryResponse rsp)
|
||||
protected void newCore(String coreName, int numShards, StoreRef storeRef, String templateName, int replicationFactor, int nodeInstance, int numNodes, String shardIds, Properties extraProperties, SolrQueryResponse rsp)
|
||||
{
|
||||
// Do nothing here otherwise we cannot spy it
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -238,10 +408,9 @@ public class AlfrescoCoreAdminHandlerTest
|
||||
public void validAndInvalidCoreNames() {
|
||||
AlfrescoCoreAdminHandler spy = spy(new AlfrescoCoreAdminHandler() {
|
||||
@Override
|
||||
protected boolean newCore(String coreName, int numShards, StoreRef storeRef, String templateName, int replicationFactor, int nodeInstance, int numNodes, String shardIds, Properties extraProperties, SolrQueryResponse rsp)
|
||||
protected void newCore(String coreName, int numShards, StoreRef storeRef, String templateName, int replicationFactor, int nodeInstance, int numNodes, String shardIds, Properties extraProperties, SolrQueryResponse rsp)
|
||||
{
|
||||
// Do nothing here otherwise we cannot spy it
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
+3
-2
@@ -37,8 +37,9 @@ import org.quartz.SchedulerException;
|
||||
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
public class AlfrescoSolrReloadTest extends AbstractAlfrescoSolrTests {
|
||||
private static Log logger = LogFactory.getLog(org.alfresco.solr.tracker.AlfrescoSolrTrackerTest.class);
|
||||
public class AlfrescoSolrReloadIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(org.alfresco.solr.tracker.AlfrescoSolrTrackerIT.class);
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
+5
-5
@@ -77,7 +77,7 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests.SolrServletRequest;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT.SolrServletRequest;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
import org.alfresco.solr.client.AclReaders;
|
||||
@@ -217,9 +217,9 @@ public class AlfrescoSolrUtils
|
||||
nodeMetaData.setAclId(acl.getId());
|
||||
nodeMetaData.setTxnId(txn.getId());
|
||||
nodeMetaData.setOwner(owner);
|
||||
nodeMetaData.setAspects(new HashSet<QName>());
|
||||
nodeMetaData.setAspects(new HashSet<>());
|
||||
nodeMetaData.setAncestors(ancestors);
|
||||
Map<QName, PropertyValue> props = new HashMap<QName, PropertyValue>();
|
||||
Map<QName, PropertyValue> props = new HashMap<>();
|
||||
props.put(ContentModel.PROP_IS_INDEXED, new StringPropertyValue("true"));
|
||||
props.put(ContentModel.PROP_CONTENT, new ContentPropertyValue(Locale.US, 0l, "UTF-8", "text/plain", null));
|
||||
nodeMetaData.setProperties(props);
|
||||
@@ -230,8 +230,8 @@ public class AlfrescoSolrUtils
|
||||
}
|
||||
nodeMetaData.setType(QName.createQName(TEST_NAMESPACE, "testSuperType"));
|
||||
nodeMetaData.setAncestors(ancestors);
|
||||
nodeMetaData.setPaths(new ArrayList<Pair<String, QName>>());
|
||||
nodeMetaData.setNamePaths(new ArrayList<List<String>>());
|
||||
nodeMetaData.setPaths(new ArrayList<>());
|
||||
nodeMetaData.setNamePaths(new ArrayList<>());
|
||||
return nodeMetaData;
|
||||
}
|
||||
/**
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
public class AlfrescoTrackerRegistrationTest extends AbstractAlfrescoSolrTests
|
||||
public class AlfrescoTrackerRegistrationIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
+1
-1
@@ -51,7 +51,7 @@ import static org.alfresco.solr.AlfrescoSolrUtils.getCore;
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class CoresCreateUpdateDistributedTest extends AbstractAlfrescoDistributedTest
|
||||
public class CoresCreateUpdateDistributedIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
final static String JETTY_SERVER_ID = "CoresCreateUpdateDistributedTest";
|
||||
+1
-1
@@ -40,7 +40,7 @@ import static org.alfresco.solr.AlfrescoSolrUtils.getCore;
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class CoresCreateViaPropertyTest extends AbstractAlfrescoDistributedTest
|
||||
public class CoresCreateViaPropertyIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
final static String JETTY_SERVER_ID = "CoresCreateViaPropertyTest";
|
||||
+1
-76
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2014 Alfresco Software Limited.
|
||||
* Copyright (C) 2005-2019 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
@@ -72,79 +72,4 @@ public class SolrDataModelTest
|
||||
Long actualId = AlfrescoSolrDataModel.parseTransactionId(id);
|
||||
assertEquals(expectedId, actualId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void smokeTestCMISModel()
|
||||
{
|
||||
AlfrescoSolrDataModel dataModel = new AlfrescoSolrDataModel();
|
||||
|
||||
// load test model containing content properties multiple
|
||||
ClassLoader cl = SolrDataModelTest.class.getClassLoader();
|
||||
InputStream modelStream = cl.getResourceAsStream("alfresco/model/dictionaryModel.xml");
|
||||
|
||||
assertNotNull(modelStream);
|
||||
M2Model model = M2Model.createModel(modelStream);
|
||||
dataModel.putModel(model);
|
||||
|
||||
modelStream = cl.getResourceAsStream("alfresco/model/cmisModel.xml");
|
||||
assertNotNull(modelStream);
|
||||
model = M2Model.createModel(modelStream);
|
||||
dataModel.putModel(model);
|
||||
|
||||
assertEquals(2, dataModel.getAlfrescoModels().size());
|
||||
|
||||
assertEquals(1, dataModel.getIndexedFieldNamesForProperty(OBJECT_ID).getFields().size());
|
||||
assertEquals(4, dataModel.getIndexedFieldNamesForProperty(NAME).getFields().size());
|
||||
assertEquals(1, dataModel.getIndexedFieldNamesForProperty(CREATION_DATE).getFields().size());
|
||||
assertEquals(0, dataModel.getIndexedFieldNamesForProperty(IS_IMMUTABLE).getFields().size());
|
||||
assertEquals(0, dataModel.getIndexedFieldNamesForProperty(IS_PRIVATE_WOKING_COPY).getFields().size());
|
||||
assertEquals(1, dataModel.getIndexedFieldNamesForProperty(CONTENT_STREAM_LENGTH).getFields().size());
|
||||
|
||||
assertEquals(1, dataModel .getQueryableFields(OBJECT_ID, null, FieldUse.FACET).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(OBJECT_ID, null, FieldUse.COMPLETION).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(OBJECT_ID, null, FieldUse.FTS).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(OBJECT_ID, null, FieldUse.ID).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(OBJECT_ID, null, FieldUse.MULTI_FACET).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(OBJECT_ID, null, FieldUse.SORT).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(OBJECT_ID, null, FieldUse.STATS).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(OBJECT_ID, null, FieldUse.SUGGESTION).getFields().size());
|
||||
|
||||
assertEquals(1, dataModel .getQueryableFields(NAME, null, FieldUse.FACET).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(NAME, null, FieldUse.COMPLETION).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(NAME, null, FieldUse.FTS).getFields().size());
|
||||
assertEquals(2, dataModel .getQueryableFields(NAME, null, FieldUse.ID).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(NAME, null, FieldUse.MULTI_FACET).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(NAME, null, FieldUse.SORT).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(NAME, null, FieldUse.STATS).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(NAME, null, FieldUse.SUGGESTION).getFields().size());
|
||||
|
||||
assertEquals(1, dataModel .getQueryableFields(CREATION_DATE, null, FieldUse.FACET).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(CREATION_DATE, null, FieldUse.COMPLETION).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(CREATION_DATE, null, FieldUse.FTS).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(CREATION_DATE, null, FieldUse.ID).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(CREATION_DATE, null, FieldUse.MULTI_FACET).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(CREATION_DATE, null, FieldUse.SORT).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(CREATION_DATE, null, FieldUse.STATS).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(CREATION_DATE, null, FieldUse.SUGGESTION).getFields().size());
|
||||
|
||||
assertEquals(1, dataModel .getQueryableFields(IS_PRIVATE_WOKING_COPY, null, FieldUse.FACET).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(IS_PRIVATE_WOKING_COPY, null, FieldUse.COMPLETION).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(IS_PRIVATE_WOKING_COPY, null, FieldUse.FTS).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(IS_PRIVATE_WOKING_COPY, null, FieldUse.ID).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(IS_PRIVATE_WOKING_COPY, null, FieldUse.MULTI_FACET).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(IS_PRIVATE_WOKING_COPY, null, FieldUse.SORT).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(IS_PRIVATE_WOKING_COPY, null, FieldUse.STATS).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(IS_PRIVATE_WOKING_COPY, null, FieldUse.SUGGESTION).getFields().size());
|
||||
|
||||
assertEquals(1, dataModel .getQueryableFields(CONTENT_STREAM_LENGTH, null, FieldUse.FACET).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(CONTENT_STREAM_LENGTH, null, FieldUse.COMPLETION).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(CONTENT_STREAM_LENGTH, null, FieldUse.FTS).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(CONTENT_STREAM_LENGTH, null, FieldUse.ID).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(CONTENT_STREAM_LENGTH, null, FieldUse.MULTI_FACET).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(CONTENT_STREAM_LENGTH, null, FieldUse.SORT).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(CONTENT_STREAM_LENGTH, null, FieldUse.STATS).getFields().size());
|
||||
assertEquals(1, dataModel .getQueryableFields(CONTENT_STREAM_LENGTH, null, FieldUse.SUGGESTION).getFields().size());
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+16
-12
@@ -4,6 +4,7 @@ import com.carrotsearch.randomizedtesting.annotations.ThreadLeakLingering;
|
||||
import org.alfresco.solr.basics.RandomSupplier;
|
||||
import org.alfresco.solr.client.SOLRAPIQueueClient;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.hadoop.util.Time;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.client.solrj.SolrClient;
|
||||
import org.apache.solr.client.solrj.embedded.JettyConfig;
|
||||
@@ -69,7 +70,7 @@ import static org.alfresco.solr.AlfrescoSolrUtils.createCoreUsingTemplate;
|
||||
* @author Michael Suzuki
|
||||
*/
|
||||
@ThreadLeakLingering(linger = 5000)
|
||||
public abstract class SolrTestInitializer extends SolrTestCaseJ4
|
||||
public abstract class SolrITInitializer extends SolrTestCaseJ4
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
@@ -225,13 +226,14 @@ public abstract class SolrTestInitializer extends SolrTestCaseJ4
|
||||
SOLRAPIQueueClient.aclMap.clear();
|
||||
SOLRAPIQueueClient.nodeMap.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a JettySolrRunner (if one didn't exist already). DOES NOT START IT.
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
private static JettySolrRunner createJetty(String jettyKey, boolean basicAuth) throws Exception
|
||||
protected static JettySolrRunner createJetty(String jettyKey, boolean basicAuth) throws Exception
|
||||
{
|
||||
if (jettyContainers.containsKey(jettyKey))
|
||||
{
|
||||
@@ -241,7 +243,7 @@ public abstract class SolrTestInitializer extends SolrTestCaseJ4
|
||||
{
|
||||
Path jettySolrHome = testDir.toPath().resolve(jettyKey);
|
||||
seedSolrHome(jettySolrHome);
|
||||
JettySolrRunner jetty = createJetty(jettySolrHome.toFile(), null, null, false, getSchemaFile(), basicAuth);
|
||||
JettySolrRunner jetty = createJetty(jettySolrHome.toFile(), null, null, false, 0, getSchemaFile(), basicAuth);
|
||||
return jetty;
|
||||
}
|
||||
}
|
||||
@@ -255,7 +257,7 @@ public abstract class SolrTestInitializer extends SolrTestCaseJ4
|
||||
* @param additionalProperties
|
||||
* @throws Exception
|
||||
*/
|
||||
private static void addCoreToJetty(String jettyKey, String sourceConfigName, String coreName, Properties additionalProperties) throws Exception
|
||||
protected static void addCoreToJetty(String jettyKey, String sourceConfigName, String coreName, Properties additionalProperties) throws Exception
|
||||
{
|
||||
Path jettySolrHome = testDir.toPath().resolve(jettyKey);
|
||||
Path coreSourceConfig = new File(getTestFilesHome() + "/"+sourceConfigName).toPath();
|
||||
@@ -297,7 +299,7 @@ public abstract class SolrTestInitializer extends SolrTestCaseJ4
|
||||
* @param jsr
|
||||
* @throws Exception
|
||||
*/
|
||||
private static void startJetty(JettySolrRunner jsr) throws Exception {
|
||||
protected static void startJetty(JettySolrRunner jsr) throws Exception {
|
||||
if (!jsr.isRunning())
|
||||
{
|
||||
jsr.start();
|
||||
@@ -308,6 +310,7 @@ public abstract class SolrTestInitializer extends SolrTestCaseJ4
|
||||
{
|
||||
boolean basicAuth = additionalProperties != null ? Boolean.parseBoolean(additionalProperties.getProperty("BasicAuth", "false")) : false;
|
||||
|
||||
|
||||
JettySolrRunner jsr = createJetty(jettyKey, basicAuth);
|
||||
jettyContainers.put(jettyKey, jsr);
|
||||
|
||||
@@ -355,7 +358,7 @@ public abstract class SolrTestInitializer extends SolrTestCaseJ4
|
||||
props.put("shard.range", ranges[i]);
|
||||
}
|
||||
|
||||
String shardKey = jettyKey+"_shard_"+i;
|
||||
String shardKey = jettyKey+"_shard_" + i + "/solrhome";
|
||||
JettySolrRunner j = createJetty(shardKey, basicAuth);
|
||||
//use the first corename specified as the Share template
|
||||
addCoreToJetty(shardKey, coreNames[0], shardname, props);
|
||||
@@ -373,7 +376,7 @@ public abstract class SolrTestInitializer extends SolrTestCaseJ4
|
||||
|
||||
protected static void destroyServers() throws Exception
|
||||
{
|
||||
List<String> solrHomes = new ArrayList<String>();
|
||||
List<String> solrHomes = new ArrayList<>();
|
||||
for (JettySolrRunner jetty : jettyContainers.values())
|
||||
{
|
||||
solrHomes.add(jetty.getSolrHome());
|
||||
@@ -406,9 +409,9 @@ public abstract class SolrTestInitializer extends SolrTestCaseJ4
|
||||
solrCollectionNameToStandaloneClient.clear();
|
||||
}
|
||||
|
||||
public static JettySolrRunner createJetty(File solrHome, String dataDir, String shardList, boolean sslEnabled, String schemaOverride, boolean basicAuth) throws Exception
|
||||
public static JettySolrRunner createJetty(File solrHome, String dataDir, String shardList, boolean sslEnabled, int port, String schemaOverride, boolean basicAuth) throws Exception
|
||||
{
|
||||
return createJetty(solrHome, dataDir, shardList, sslEnabled, schemaOverride, useExplicitNodeNames, basicAuth);
|
||||
return createJetty(solrHome, dataDir, shardList, sslEnabled, port, schemaOverride, useExplicitNodeNames, basicAuth);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -417,12 +420,13 @@ public abstract class SolrTestInitializer extends SolrTestCaseJ4
|
||||
* @param solrHome
|
||||
* @param dataDir
|
||||
* @param shardList
|
||||
* @param port
|
||||
* @param schemaOverride
|
||||
* @param explicitCoreNodeName
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static JettySolrRunner createJetty(File solrHome, String dataDir, String shardList, boolean sslEnabled,
|
||||
public static JettySolrRunner createJetty(File solrHome, String dataDir, String shardList, boolean sslEnabled, int port,
|
||||
String schemaOverride, boolean explicitCoreNodeName, boolean basicAuth) throws Exception
|
||||
{
|
||||
Properties props = new Properties();
|
||||
@@ -445,10 +449,10 @@ public abstract class SolrTestInitializer extends SolrTestCaseJ4
|
||||
|
||||
if(basicAuth) {
|
||||
System.out.println("###### adding basic auth ######");
|
||||
config = JettyConfig.builder().setContext("/solr").withFilter(BasicAuthFilter.class, "/sql/*").stopAtShutdown(true).withSSLConfig(sslConfig).build();
|
||||
config = JettyConfig.builder().setContext("/solr").setPort(port).withFilter(BasicAuthFilter.class, "/sql/*").stopAtShutdown(true).withSSLConfig(sslConfig).build();
|
||||
} else {
|
||||
System.out.println("###### no basic auth ######");
|
||||
config = JettyConfig.builder().setContext("/solr").stopAtShutdown(true).withSSLConfig(sslConfig).build();
|
||||
config = JettyConfig.builder().setContext("/solr").setPort(port).stopAtShutdown(true).withSSLConfig(sslConfig).build();
|
||||
}
|
||||
|
||||
JettySolrRunner jetty = new JettySolrRunner(solrHome.getAbsolutePath(), props, config);
|
||||
@@ -24,8 +24,6 @@ package org.alfresco.solr;
|
||||
*/
|
||||
public interface SolrTestFiles
|
||||
{
|
||||
public final String TEST_FILES_LOCATION = "target/test-classes/test-files";
|
||||
public final String TEST_SOLR_COLLECTION = TEST_FILES_LOCATION + "/collection1";
|
||||
public final String TEST_SOLR_CONF = TEST_SOLR_COLLECTION + "/conf/";
|
||||
public final String TEMPLATE_CONF = TEST_FILES_LOCATION + "/templates/%s/conf/";
|
||||
String TEST_FILES_LOCATION = "target/test-classes/test-files";
|
||||
String TEST_EXECUTION_FOLDER = "target/test-execution-folder";
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ import static org.alfresco.solr.AlfrescoSolrUtils.*;
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class TemplatesDistributedTest extends AbstractAlfrescoDistributedTest
|
||||
public class TemplatesDistributedIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
final static String JETTY_SERVER_ID = "TemplatesDistributedTest";
|
||||
+291
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.solr.content;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Solr ContentStore {@link ChangeSet} test case.
|
||||
*
|
||||
* @author Andrea Gazzarini
|
||||
* @since 1.5
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SolrContentStoreChangeSetTest
|
||||
{
|
||||
private ChangeSet changeSet;
|
||||
private final String contentStoreRootFolder = "/tmp";
|
||||
private final File rootFolder = new File(contentStoreRootFolder, ChangeSet.CHANGESETS_ROOT_FOLDER_NAME);
|
||||
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
changeSet = new ChangeSet.Builder().withContentStoreRoot(contentStoreRootFolder).build();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws IOException
|
||||
{
|
||||
changeSet.close();
|
||||
FileUtils.cleanDirectory(rootFolder);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void nullContentStoreRootFolder_shouldThrowAnException()
|
||||
{
|
||||
new ChangeSet.Builder().withContentStoreRoot(null).build();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void nonWriteableContentStoreRootFolder_shouldThrowAnException()
|
||||
{
|
||||
new ChangeSet.Builder().withContentStoreRoot("/root").build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newAddOrReplaceRecord_shouldRemovePreviousDeletion()
|
||||
{
|
||||
String path ="some/random/dbid.gz";
|
||||
|
||||
assertTrue(changeSet.deletes.isEmpty());
|
||||
assertTrue(changeSet.adds.isEmpty());
|
||||
|
||||
changeSet.delete(path);
|
||||
|
||||
assertTrue(String.valueOf(changeSet.deletes), changeSet.deletes.contains(path));
|
||||
assertTrue(String.valueOf(changeSet.adds), changeSet.adds.isEmpty());
|
||||
|
||||
changeSet.addOrReplace(path);
|
||||
|
||||
assertTrue(String.valueOf(changeSet.deletes), changeSet.deletes.isEmpty());
|
||||
assertTrue(String.valueOf(changeSet.adds),changeSet.adds.contains(path));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deletedRecord_shouldRemovePreviousAdd()
|
||||
{
|
||||
String path ="some/random/dbid.gz";
|
||||
|
||||
assertTrue(changeSet.deletes.isEmpty());
|
||||
assertTrue(changeSet.adds.isEmpty());
|
||||
|
||||
changeSet.addOrReplace(path);
|
||||
|
||||
assertTrue(String.valueOf(changeSet.deletes), changeSet.deletes.isEmpty());
|
||||
assertTrue(String.valueOf(changeSet.adds), changeSet.adds.contains(path));
|
||||
|
||||
changeSet.delete(path);
|
||||
|
||||
assertTrue(String.valueOf(changeSet.deletes), changeSet.deletes.contains(path));
|
||||
assertTrue(String.valueOf(changeSet.adds),changeSet.adds.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transientChangeset_doesNothingOnFlush() throws IOException
|
||||
{
|
||||
ChangeSet changeset = new ChangeSet.Builder().build();
|
||||
changeset.addOrReplace("A");
|
||||
changeset.delete("B");
|
||||
|
||||
assertEquals(1, changeset.deletes.size());
|
||||
assertEquals(1, changeset.adds.size());
|
||||
|
||||
changeset.flush();
|
||||
|
||||
assertEquals(1, changeset.deletes.size());
|
||||
assertEquals(1, changeset.adds.size());
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void emptyChangeset_isImmutableDoesntAllowAdds()
|
||||
{
|
||||
ChangeSet changeset = new ChangeSet.Builder().empty().build();
|
||||
changeset.addOrReplace("A");
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void emptyChangeset_isImmutableDoesntAllowDeletes()
|
||||
{
|
||||
ChangeSet changeset = new ChangeSet.Builder().empty().build();
|
||||
changeset.delete("A");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lastCommittedVersionNotPresentAtVeryBeginning()
|
||||
{
|
||||
assertEquals(SolrContentStore.NO_VERSION_AVAILABLE, changeSet.getLastCommittedVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lastCommittedVersionNotAvailable_shouldReturnNO_AVAILABLE_VERSION() throws IOException
|
||||
{
|
||||
changeSet.selectEverything = mock(Query.class);
|
||||
when(changeSet.selectEverything.rewrite(any(IndexReader.class))).thenThrow(new RuntimeException());
|
||||
assertEquals(SolrContentStore.NO_VERSION_AVAILABLE, changeSet.getLastCommittedVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flushDoesNothingIfThereAreNoChanges() throws IOException
|
||||
{
|
||||
assertEquals(SolrContentStore.NO_VERSION_AVAILABLE, changeSet.getLastCommittedVersion());
|
||||
|
||||
changeSet.flush();
|
||||
|
||||
assertEquals(SolrContentStore.NO_VERSION_AVAILABLE, changeSet.getLastCommittedVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidOrUnknownVersion() throws IOException
|
||||
{
|
||||
assertEquals(SolrContentStore.NO_VERSION_AVAILABLE, changeSet.getLastCommittedVersion());
|
||||
|
||||
assertTrue(changeSet.isUnknownVersion(SolrContentStore.NO_VERSION_AVAILABLE));
|
||||
assertTrue(changeSet.isUnknownVersion(SolrContentStore.NO_VERSION_AVAILABLE - 1L));
|
||||
assertTrue(changeSet.isUnknownVersion(System.currentTimeMillis()));
|
||||
|
||||
changeSet.addOrReplace("A1");
|
||||
changeSet.addOrReplace("A2");
|
||||
changeSet.delete("A3");
|
||||
changeSet.delete("A1");
|
||||
|
||||
changeSet.flush();
|
||||
|
||||
long lastCommittedVersionAfterFirstFlush = changeSet.getLastCommittedVersion();
|
||||
assertNotEquals(SolrContentStore.NO_VERSION_AVAILABLE, lastCommittedVersionAfterFirstFlush);
|
||||
|
||||
assertTrue(changeSet.isUnknownVersion(System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validVersion() throws IOException
|
||||
{
|
||||
assertEquals(SolrContentStore.NO_VERSION_AVAILABLE, changeSet.getLastCommittedVersion());
|
||||
|
||||
assertTrue(changeSet.isUnknownVersion(System.currentTimeMillis()));
|
||||
|
||||
changeSet.addOrReplace("A1");
|
||||
changeSet.addOrReplace("A2");
|
||||
changeSet.delete("A3");
|
||||
changeSet.delete("A1");
|
||||
|
||||
changeSet.flush();
|
||||
|
||||
long lastCommittedVersionAfterFirstFlush = changeSet.getLastCommittedVersion();
|
||||
|
||||
changeSet.addOrReplace("B1");
|
||||
changeSet.addOrReplace("B2");
|
||||
changeSet.delete("B3");
|
||||
changeSet.delete("B1");
|
||||
|
||||
changeSet.flush();
|
||||
|
||||
long lastCommittedVersionAfterSecondFlush = changeSet.getLastCommittedVersion();
|
||||
assertNotEquals(lastCommittedVersionAfterSecondFlush, lastCommittedVersionAfterFirstFlush);
|
||||
|
||||
assertFalse(changeSet.isUnknownVersion(lastCommittedVersionAfterFirstFlush));
|
||||
assertFalse(changeSet.isUnknownVersion(lastCommittedVersionAfterSecondFlush));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inCaseOfFailure_inputVersionIsConsideredUnknown() throws IOException
|
||||
{
|
||||
assertEquals(SolrContentStore.NO_VERSION_AVAILABLE, changeSet.getLastCommittedVersion());
|
||||
|
||||
assertTrue(changeSet.isUnknownVersion(System.currentTimeMillis()));
|
||||
|
||||
changeSet.addOrReplace("A1");
|
||||
changeSet.addOrReplace("A2");
|
||||
changeSet.delete("A3");
|
||||
changeSet.delete("A1");
|
||||
|
||||
changeSet.flush();
|
||||
|
||||
long lastCommittedVersion = changeSet.getLastCommittedVersion();
|
||||
|
||||
// Force a NPE exception...
|
||||
changeSet.searcher = null;
|
||||
|
||||
// ...so a valid version is considered unknown even if it is valid
|
||||
assertTrue(changeSet.isUnknownVersion(lastCommittedVersion));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistentChangesetsAreMergedBeforeReturningToRequestor() throws IOException
|
||||
{
|
||||
assertEquals(SolrContentStore.NO_VERSION_AVAILABLE, changeSet.getLastCommittedVersion());
|
||||
|
||||
changeSet.addOrReplace("A1");
|
||||
changeSet.addOrReplace("A2");
|
||||
changeSet.delete("A3");
|
||||
changeSet.delete("A1");
|
||||
|
||||
changeSet.flush();
|
||||
|
||||
long lastCommittedVersionAfterFirstFlush = changeSet.getLastCommittedVersion();
|
||||
assertNotEquals(SolrContentStore.NO_VERSION_AVAILABLE, lastCommittedVersionAfterFirstFlush);
|
||||
|
||||
changeSet.addOrReplace("A1");
|
||||
changeSet.addOrReplace("A3");
|
||||
changeSet.delete("A4");
|
||||
|
||||
changeSet.flush();
|
||||
|
||||
long lastCommittedVersionAfterSecondFlush = changeSet.getLastCommittedVersion();
|
||||
assertNotEquals(lastCommittedVersionAfterFirstFlush, lastCommittedVersionAfterSecondFlush);
|
||||
|
||||
ChangeSet changesSinceTheVeryBeginning = changeSet.since(SolrContentStore.NO_VERSION_AVAILABLE);
|
||||
|
||||
// ADDS = [A1, A2, A3]
|
||||
// DELS = [A4]
|
||||
assertEquals(3, changesSinceTheVeryBeginning.adds.size());
|
||||
assertEquals(1, changesSinceTheVeryBeginning.deletes.size());
|
||||
assertTrue(changesSinceTheVeryBeginning.adds.contains("A1"));
|
||||
assertTrue(changesSinceTheVeryBeginning.adds.contains("A2"));
|
||||
assertTrue(changesSinceTheVeryBeginning.adds.contains("A3"));
|
||||
assertTrue(changesSinceTheVeryBeginning.deletes.contains("A4"));
|
||||
|
||||
ChangeSet changesAfterSecondFlush = changeSet.since(lastCommittedVersionAfterFirstFlush);
|
||||
|
||||
// ADDS = [A1, A3]
|
||||
// DELS = [A4]
|
||||
assertEquals(2, changesAfterSecondFlush.adds.size());
|
||||
assertEquals(1, changesAfterSecondFlush.deletes.size());
|
||||
assertTrue(changesAfterSecondFlush.adds.contains("A1"));
|
||||
assertTrue(changesAfterSecondFlush.adds.contains("A3"));
|
||||
assertTrue(changesAfterSecondFlush.deletes.contains("A4"));
|
||||
}
|
||||
}
|
||||
+284
-156
@@ -18,47 +18,245 @@
|
||||
*/
|
||||
package org.alfresco.solr.content;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.alfresco.repo.content.ContentContext;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.alfresco.solr.client.NodeMetaData;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.solr.common.SolrInputDocument;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests {@link SolrContentStoreTest}
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 5.0
|
||||
* @author Andrea Gazzarini
|
||||
* @since 1.5
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SolrContentStoreTest
|
||||
{
|
||||
private static final String DEFAULT_TENANT = "_DEFAULT_";
|
||||
private String solrHome = new File("./target/contentstoretest/").getAbsolutePath();
|
||||
private long dbid = 111;
|
||||
private String tenant = "me";
|
||||
|
||||
private String solrHome;
|
||||
private SolrContentStore contentStore;
|
||||
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
solrHome = new File("./target/contentstoretest/" + System.currentTimeMillis()).getAbsolutePath();
|
||||
contentStore = new SolrContentStore(solrHome);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws IOException
|
||||
{
|
||||
contentStore.close();
|
||||
|
||||
File rootDir = new File(new SolrContentStore(solrHome).getRootLocation());
|
||||
FileUtils.deleteDirectory(rootDir);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void atVeryBeginningAccessModeIsNotSet()
|
||||
{
|
||||
assertSame(contentStore.notYetSet, contentStore.currentAccessMode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAccessModeIsNotSetMethodCallsThrowsExceptionOrDoNothing()
|
||||
{
|
||||
assertSame(contentStore.notYetSet, contentStore.currentAccessMode);
|
||||
|
||||
expectIllegalState(contentStore::getLastCommittedVersion);
|
||||
expectIllegalState(contentStore::setLastCommittedVersion, System.currentTimeMillis());
|
||||
expectIllegalState(contentStore::getChanges, System.currentTimeMillis());
|
||||
expectIllegalState(contentStore::removeDocFromContentStore, mock(NodeMetaData.class));
|
||||
expectIllegalState(contentStore::storeDocOnSolrContentStore, mock(NodeMetaData.class), mock(SolrInputDocument.class));
|
||||
|
||||
try
|
||||
{
|
||||
contentStore.flushChangeSet();
|
||||
fail();
|
||||
}
|
||||
catch (IOException exception)
|
||||
{
|
||||
fail();
|
||||
}
|
||||
catch(IllegalStateException expected)
|
||||
{
|
||||
// Nothing to be done here
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
contentStore.storeDocOnSolrContentStore(DEFAULT_TENANT, System.currentTimeMillis(), mock(SolrInputDocument.class));
|
||||
fail();
|
||||
}
|
||||
catch(IllegalStateException expected)
|
||||
{
|
||||
// Nothing to be done here
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lastCommittedVersionInReadOnlyModeNotFound()
|
||||
{
|
||||
contentStore.toggleReadOnlyMode(true);
|
||||
|
||||
assertEquals(SolrContentStore.NO_VERSION_AVAILABLE, contentStore.getLastCommittedVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lastCommittedVersionInReadOnlyModeNotFoundBecauseException() throws IOException
|
||||
{
|
||||
contentStore.toggleReadOnlyMode(true);
|
||||
|
||||
Files.write(new File(contentStore.getRootLocation(), ".version").toPath(), "NAN".getBytes());
|
||||
|
||||
assertEquals(SolrContentStore.NO_VERSION_AVAILABLE, contentStore.getLastCommittedVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lastCommittedVersionInReadOnlyModeNotFoundBecauseFileIsEmpty() throws IOException
|
||||
{
|
||||
contentStore.toggleReadOnlyMode(true);
|
||||
|
||||
File emptyVersionFile = new File(contentStore.getRootLocation(), ".version");
|
||||
emptyVersionFile.createNewFile();
|
||||
|
||||
assertEquals(SolrContentStore.NO_VERSION_AVAILABLE, contentStore.getLastCommittedVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLastCommittedVersionInReadOnlyMode() throws IOException
|
||||
{
|
||||
contentStore.toggleReadOnlyMode(true);
|
||||
|
||||
long expectedLastCommittedVersion = System.currentTimeMillis();
|
||||
|
||||
Files.write(new File(contentStore.getRootLocation(), ".version").toPath(), Long.toString(expectedLastCommittedVersion).getBytes());
|
||||
|
||||
assertEquals(expectedLastCommittedVersion, contentStore.getLastCommittedVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setLastCommittedVersionInReadOnlyMode()
|
||||
{
|
||||
contentStore.toggleReadOnlyMode(true);
|
||||
|
||||
long expectedLastCommittedVersion = System.currentTimeMillis();
|
||||
contentStore.setLastCommittedVersion(expectedLastCommittedVersion);
|
||||
|
||||
File versionFile = new File(contentStore.getRootLocation(), ".version");
|
||||
assertTrue(versionFile.canRead());
|
||||
|
||||
assertEquals(expectedLastCommittedVersion, contentStore.getLastCommittedVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getChangesInReadOnlyModeReturnsAnEmptyMap()
|
||||
{
|
||||
contentStore.toggleReadOnlyMode(true);
|
||||
assertEquals(Collections.<String, List<Map<String, Object>>>emptyMap(), contentStore.getChanges(System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transitionFromNotSetToReadOnlyMode()
|
||||
{
|
||||
assertSame(contentStore.notYetSet, contentStore.currentAccessMode);
|
||||
|
||||
contentStore.toggleReadOnlyMode(true);
|
||||
|
||||
assertSame(contentStore.readOnly, contentStore.currentAccessMode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transitionFromNotSetToReadWriteMode()
|
||||
{
|
||||
assertSame(contentStore.notYetSet, contentStore.currentAccessMode);
|
||||
|
||||
contentStore.toggleReadOnlyMode(false);
|
||||
|
||||
assertSame(contentStore.readWrite, contentStore.currentAccessMode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transitionFromReadOnlyToReadWriteMode()
|
||||
{
|
||||
assertSame(contentStore.notYetSet, contentStore.currentAccessMode);
|
||||
|
||||
contentStore.toggleReadOnlyMode(true);
|
||||
|
||||
assertSame(contentStore.readOnly, contentStore.currentAccessMode);
|
||||
|
||||
contentStore.toggleReadOnlyMode(false);
|
||||
|
||||
assertSame(contentStore.readWrite, contentStore.currentAccessMode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transitionFromReadOnlyToReadOnlyHasNoEffect()
|
||||
{
|
||||
assertSame(contentStore.notYetSet, contentStore.currentAccessMode);
|
||||
|
||||
contentStore.toggleReadOnlyMode(true);
|
||||
|
||||
assertSame(contentStore.readOnly, contentStore.currentAccessMode);
|
||||
|
||||
contentStore.toggleReadOnlyMode(true);
|
||||
|
||||
assertSame(contentStore.readOnly, contentStore.currentAccessMode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transitionFromReadWriteToReadOnlyModeHasNoEffect()
|
||||
{
|
||||
assertSame(contentStore.notYetSet, contentStore.currentAccessMode);
|
||||
|
||||
contentStore.toggleReadOnlyMode(false);
|
||||
|
||||
assertSame(contentStore.readWrite, contentStore.currentAccessMode);
|
||||
|
||||
contentStore.toggleReadOnlyMode(true);
|
||||
|
||||
assertSame(contentStore.readWrite, contentStore.currentAccessMode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transitionFromReadWriteToReadWriteHasNoEffect()
|
||||
{
|
||||
assertSame(contentStore.notYetSet, contentStore.currentAccessMode);
|
||||
|
||||
contentStore.toggleReadOnlyMode(false);
|
||||
|
||||
assertSame(contentStore.readWrite, contentStore.currentAccessMode);
|
||||
|
||||
contentStore.toggleReadOnlyMode(false);
|
||||
|
||||
assertSame(contentStore.readWrite, contentStore.currentAccessMode);
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeException.class)
|
||||
public void contentStoreCreation_solrHomeNull_shouldThrowException()
|
||||
{
|
||||
@@ -117,180 +315,110 @@ public class SolrContentStoreTest
|
||||
Assert.assertThat(solrContentStore.getRootLocation(), is(solrHome + "/" + SolrContentStore.CONTENT_STORE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a content context using a string. The URL part will be the same if the
|
||||
* data provided is the same.
|
||||
*/
|
||||
private ContentContext createContentContext(String data)
|
||||
{
|
||||
return SolrContentUrlBuilder.start().add("data", data).getContentContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rootLocation()
|
||||
{
|
||||
SolrContentStore store = new SolrContentStore(solrHome);
|
||||
File rootDir = new File(store.getRootLocation());
|
||||
Assert.assertTrue(rootDir.exists());
|
||||
Assert.assertTrue(rootDir.isDirectory());
|
||||
File rootDir = new File(contentStore.getRootLocation());
|
||||
|
||||
assertTrue(rootDir.exists());
|
||||
assertTrue(rootDir.isDirectory());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWriter()
|
||||
|
||||
@Test
|
||||
public void storeDocOnSolrContentStore()
|
||||
{
|
||||
SolrContentStore store = new SolrContentStore(solrHome);
|
||||
contentStore.toggleReadOnlyMode(false);
|
||||
|
||||
ContentContext ctx = createContentContext("abc");
|
||||
ContentWriter writer = store.getWriter(ctx);
|
||||
String url = writer.getContentUrl();
|
||||
SolrInputDocument doc = mock(SolrInputDocument.class);
|
||||
long dbid = 111;
|
||||
String tenant = "me";
|
||||
SolrInputDocument document = contentStore.retrieveDocFromSolrContentStore(tenant, dbid);
|
||||
Assert.assertNull(document);
|
||||
|
||||
Assert.assertNotNull(url);
|
||||
Assert.assertEquals("URL of the context does not match the writer URL. ", ctx.getContentUrl(), url);
|
||||
contentStore.storeDocOnSolrContentStore(tenant, dbid, doc);
|
||||
document = contentStore.retrieveDocFromSolrContentStore(tenant, dbid);
|
||||
Assert.assertNotNull(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentByString()
|
||||
@Test
|
||||
public void storeDocOnSolrContentStoreNodeMetaData()
|
||||
{
|
||||
SolrContentStore store = new SolrContentStore(solrHome);
|
||||
contentStore.toggleReadOnlyMode(false);
|
||||
SolrInputDocument doc = mock(SolrInputDocument.class);
|
||||
NodeMetaData nodeMetaData = mock(NodeMetaData.class);
|
||||
SolrInputDocument document = contentStore.retrieveDocFromSolrContentStore(DEFAULT_TENANT, 0);
|
||||
Assert.assertNull(document);
|
||||
|
||||
ContentContext ctx = createContentContext("abc");
|
||||
ContentWriter writer = store.getWriter(ctx);
|
||||
contentStore.storeDocOnSolrContentStore(nodeMetaData, doc);
|
||||
document = contentStore.retrieveDocFromSolrContentStore(DEFAULT_TENANT, 0);
|
||||
Assert.assertNotNull(document);
|
||||
}
|
||||
|
||||
File file = new File(store.getRootLocation() + "/" + writer.getContentUrl().replace("solr://", ""));
|
||||
Assert.assertFalse("File was created before anything was written", file.exists());
|
||||
@Test
|
||||
public void removeDocFromContentStore()
|
||||
{
|
||||
contentStore.toggleReadOnlyMode(false);
|
||||
SolrInputDocument doc = mock(SolrInputDocument.class);
|
||||
NodeMetaData nodeMetaData = mock(NodeMetaData.class);
|
||||
contentStore.storeDocOnSolrContentStore(nodeMetaData, doc);
|
||||
SolrInputDocument document = contentStore.retrieveDocFromSolrContentStore(DEFAULT_TENANT, 0);
|
||||
Assert.assertNotNull(document);
|
||||
|
||||
String content = "Quick brown fox jumps over the lazy dog.";
|
||||
writer.putContent(content);
|
||||
Assert.assertTrue("File was not created.", file.exists());
|
||||
contentStore.removeDocFromContentStore(nodeMetaData);
|
||||
document = contentStore.retrieveDocFromSolrContentStore(DEFAULT_TENANT, 0);
|
||||
Assert.assertNull(document);
|
||||
}
|
||||
|
||||
private void expectIllegalState(Supplier<?> function)
|
||||
{
|
||||
try
|
||||
{
|
||||
writer.putContent("Should not work");
|
||||
function.get();
|
||||
fail();
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
catch (IllegalStateException expected)
|
||||
{
|
||||
// Expected
|
||||
// Nothing to do, this is expected
|
||||
}
|
||||
|
||||
// Now get the reader
|
||||
ContentReader reader = store.getReader(ctx.getContentUrl());
|
||||
Assert.assertNotNull(reader);
|
||||
Assert.assertTrue(reader.exists());
|
||||
|
||||
Assert.assertEquals(content, reader.getContentString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentByStream() throws Exception
|
||||
private <T> void expectIllegalState(Consumer<T> function, T arg)
|
||||
{
|
||||
SolrContentStore store = new SolrContentStore(solrHome);
|
||||
|
||||
ContentContext ctx = createContentContext("abc");
|
||||
ContentWriter writer = store.getWriter(ctx);
|
||||
|
||||
byte[] bytes = new byte[] { 1, 7, 13 };
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
|
||||
writer.putContent(bis);
|
||||
|
||||
// Now get the reader
|
||||
ContentReader reader = store.getReader(ctx.getContentUrl());
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(3);
|
||||
reader.getContent(bos);
|
||||
Assert.assertEquals(bytes[0], bos.toByteArray()[0]);
|
||||
Assert.assertEquals(bytes[1], bos.toByteArray()[1]);
|
||||
Assert.assertEquals(bytes[2], bos.toByteArray()[2]);
|
||||
try
|
||||
{
|
||||
function.accept(arg);
|
||||
fail();
|
||||
}
|
||||
catch (IllegalStateException expected)
|
||||
{
|
||||
// Nothing to do, this is expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delete() throws Exception
|
||||
private <I,O> void expectIllegalState(Function<I,O> function, I arg)
|
||||
{
|
||||
SolrContentStore store = new SolrContentStore(solrHome);
|
||||
|
||||
ContentContext ctx = createContentContext("abc");
|
||||
String url = ctx.getContentUrl();
|
||||
ContentWriter writer = store.getWriter(ctx);
|
||||
writer.putContent("Content goes here.");
|
||||
|
||||
// Check the reader
|
||||
ContentReader reader = store.getReader(url);
|
||||
Assert.assertNotNull(reader);
|
||||
Assert.assertTrue(reader.exists());
|
||||
|
||||
// Delete
|
||||
store.delete(url);
|
||||
reader = store.getReader(url);
|
||||
Assert.assertNotNull(reader);
|
||||
Assert.assertFalse(reader.exists());
|
||||
|
||||
// Delete when already gone; should just not fail
|
||||
store.delete(url);
|
||||
try
|
||||
{
|
||||
function.apply(arg);
|
||||
fail();
|
||||
}
|
||||
catch (IllegalStateException expected)
|
||||
{
|
||||
// Nothing to do, this is expected
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A demonstration of how the store might be used.
|
||||
*/
|
||||
@Test
|
||||
public void exampleUsage()
|
||||
private <A, B> void expectIllegalState(BiConsumer<A, B> function, A arg1, B arg2)
|
||||
{
|
||||
SolrContentStore store = new SolrContentStore(solrHome);
|
||||
|
||||
String tenant = "alfresco.com";
|
||||
long dbId = 12345;
|
||||
String otherData = "sdfklsfdl";
|
||||
|
||||
ContentContext ctxWrite = SolrContentUrlBuilder.start()
|
||||
.add(SolrContentUrlBuilder.KEY_DB_ID, String.valueOf(dbId)).add(SolrContentUrlBuilder.KEY_TENANT, tenant)
|
||||
.add("otherData", otherData).getContentContext();
|
||||
ContentWriter writer = store.getWriter(ctxWrite);
|
||||
writer.putContent("a document in plain text");
|
||||
|
||||
// The URL can be reliably rebuilt in any order
|
||||
String urlRead = SolrContentUrlBuilder.start().add("otherData", otherData)
|
||||
.add(SolrContentUrlBuilder.KEY_TENANT, tenant).add(SolrContentUrlBuilder.KEY_DB_ID, String.valueOf(dbId))
|
||||
.get();
|
||||
ContentReader reader = store.getReader(urlRead);
|
||||
String documentText = reader.getContentString();
|
||||
|
||||
Assert.assertEquals("a document in plain text", documentText);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void storeDocOnSolrContentStore() throws IOException
|
||||
{
|
||||
SolrContentStore solrContentStore = new SolrContentStore(solrHome);
|
||||
SolrInputDocument doc = Mockito.mock(SolrInputDocument.class);
|
||||
SolrInputDocument document = solrContentStore.retrieveDocFromSolrContentStore(tenant, dbid);
|
||||
Assert.assertNull(document);
|
||||
solrContentStore.storeDocOnSolrContentStore(tenant, dbid, doc);
|
||||
document = solrContentStore.retrieveDocFromSolrContentStore(tenant, dbid);
|
||||
Assert.assertNotNull(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void storeDocOnSolrContentStoreNodeMetaData() throws IOException
|
||||
{
|
||||
SolrContentStore solrContentStore = new SolrContentStore(solrHome);
|
||||
SolrInputDocument doc = Mockito.mock(SolrInputDocument.class);
|
||||
NodeMetaData nodeMetaData = Mockito.mock(NodeMetaData.class);
|
||||
SolrInputDocument document = solrContentStore.retrieveDocFromSolrContentStore(DEFAULT_TENANT, 0);
|
||||
Assert.assertNull(document);
|
||||
solrContentStore.storeDocOnSolrContentStore(nodeMetaData, doc);
|
||||
document = solrContentStore.retrieveDocFromSolrContentStore(DEFAULT_TENANT, 0);
|
||||
Assert.assertNotNull(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeDocFromContentStore() throws IOException
|
||||
{
|
||||
SolrContentStore solrContentStore = new SolrContentStore(solrHome);
|
||||
SolrInputDocument doc = Mockito.mock(SolrInputDocument.class);
|
||||
NodeMetaData nodeMetaData = Mockito.mock(NodeMetaData.class);
|
||||
solrContentStore.storeDocOnSolrContentStore(nodeMetaData, doc);
|
||||
SolrInputDocument document = solrContentStore.retrieveDocFromSolrContentStore(DEFAULT_TENANT, 0);
|
||||
Assert.assertNotNull(document);
|
||||
solrContentStore.removeDocFromContentStore(nodeMetaData);
|
||||
document = solrContentStore.retrieveDocFromSolrContentStore(DEFAULT_TENANT, 0);
|
||||
Assert.assertNull(document);
|
||||
try
|
||||
{
|
||||
function.accept(arg1, arg2);
|
||||
fail();
|
||||
}
|
||||
catch (IllegalStateException expected)
|
||||
{
|
||||
// Nothing to do, this is expected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-5
@@ -21,7 +21,7 @@ package org.alfresco.solr.content;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
/**
|
||||
* Tests {@link SolrContentUrlBuilder}
|
||||
@@ -85,12 +85,12 @@ public class SolrContentUrlBuilderTest
|
||||
.add(SolrContentUrlBuilder.KEY_DB_ID, "12345")
|
||||
.get();
|
||||
Assert.assertEquals(url1, url1Check);
|
||||
|
||||
String url2 = SolrContentUrlBuilder
|
||||
.start()
|
||||
.add(SolrContentUrlBuilder.KEY_DB_ID, "123456")
|
||||
.get();
|
||||
Assert.assertNotEquals(url1, url2);
|
||||
|
||||
Assert.assertEquals("Incorrect URL: " + url1, "solr://default/db/1234/5.gz", url1);
|
||||
}
|
||||
|
||||
@@ -108,7 +108,6 @@ public class SolrContentUrlBuilderTest
|
||||
.add(SolrContentUrlBuilder.KEY_TENANT, "bob")
|
||||
.get();
|
||||
Assert.assertEquals(url1, url1Check);
|
||||
|
||||
Assert.assertEquals("Incorrect URL: " + url1, "solr://bob/db/1234/56.gz", url1);
|
||||
}
|
||||
|
||||
@@ -126,7 +125,6 @@ public class SolrContentUrlBuilderTest
|
||||
.add(SolrContentUrlBuilder.KEY_TENANT, "bob")
|
||||
.get();
|
||||
Assert.assertEquals(url1, url1Check);
|
||||
|
||||
Assert.assertEquals("Incorrect URL: " + url1, "solr://bob/acl/1234/56.gz", url1);
|
||||
}
|
||||
|
||||
@@ -146,7 +144,6 @@ public class SolrContentUrlBuilderTest
|
||||
.add(SolrContentUrlBuilder.KEY_TENANT, "bob")
|
||||
.get();
|
||||
Assert.assertEquals(url1, url1Check);
|
||||
|
||||
Assert.assertEquals("Incorrect URL: " + url1, "solr://bob/db/5432/1.gz", url1);
|
||||
}
|
||||
|
||||
@@ -164,6 +161,7 @@ public class SolrContentUrlBuilderTest
|
||||
{
|
||||
// Expected
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SolrContentUrlBuilder
|
||||
@@ -176,6 +174,7 @@ public class SolrContentUrlBuilderTest
|
||||
{
|
||||
// Expected
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SolrContentUrlBuilder
|
||||
@@ -188,6 +187,7 @@ public class SolrContentUrlBuilderTest
|
||||
{
|
||||
// Expected
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SolrContentUrlBuilder
|
||||
@@ -200,6 +200,7 @@ public class SolrContentUrlBuilderTest
|
||||
{
|
||||
// Expected
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SolrContentUrlBuilder
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package org.alfresco.solr.content;
|
||||
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class SolrContentWriterTest
|
||||
{
|
||||
private String solrHome = new File("./target/contentwriter/").getAbsolutePath();
|
||||
|
||||
@After
|
||||
public void tearDown() throws IOException
|
||||
{
|
||||
File rootDir = new File(solrHome);
|
||||
FileUtils.deleteDirectory(rootDir);
|
||||
}
|
||||
|
||||
private ContentWriter getContentWriter(String name)
|
||||
{
|
||||
return new SolrFileContentWriter(new File(solrHome, name), solrHome + "/" + name);
|
||||
}
|
||||
|
||||
private ContentReader getContentReader(String name)
|
||||
{
|
||||
return new SolrFileContentReader(new File(solrHome, name), solrHome + "/" + name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentByString()
|
||||
{
|
||||
String filename = "abc";
|
||||
ContentWriter writer = getContentWriter(filename);
|
||||
|
||||
File file = new File(solrHome, filename);
|
||||
Assert.assertFalse("File was created before anything was written", file.exists());
|
||||
|
||||
String content = "Quick brown fox jumps over the lazy dog.";
|
||||
writer.putContent(content);
|
||||
Assert.assertTrue("File was not created.", file.exists());
|
||||
|
||||
try
|
||||
{
|
||||
writer.putContent("Should not work");
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
{
|
||||
// Expected
|
||||
}
|
||||
|
||||
// Now get the reader
|
||||
ContentReader reader = getContentReader(filename);
|
||||
Assert.assertNotNull(reader);
|
||||
Assert.assertTrue(reader.exists());
|
||||
|
||||
Assert.assertEquals(content, reader.getContentString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentByStream()
|
||||
{
|
||||
|
||||
String filename = "cbs";
|
||||
ContentWriter writer = getContentWriter(filename);
|
||||
|
||||
byte[] bytes = new byte[] { 1, 7, 13 };
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
|
||||
writer.putContent(bis);
|
||||
|
||||
// Now get the reader
|
||||
ContentReader reader = getContentReader(filename);
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(3);
|
||||
reader.getContent(bos);
|
||||
Assert.assertEquals(bytes[0], bos.toByteArray()[0]);
|
||||
Assert.assertEquals(bytes[1], bos.toByteArray()[1]);
|
||||
Assert.assertEquals(bytes[2], bos.toByteArray()[2]);
|
||||
}
|
||||
}
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2019 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.solr.handler;
|
||||
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
import org.alfresco.solr.client.AclReaders;
|
||||
import org.alfresco.solr.client.Node;
|
||||
import org.alfresco.solr.client.NodeMetaData;
|
||||
import org.alfresco.solr.client.SOLRAPIQueueClient;
|
||||
import org.alfresco.solr.client.Transaction;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.hadoop.util.Time;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.client.solrj.SolrClient;
|
||||
import org.apache.solr.client.solrj.embedded.JettySolrRunner;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.getAcl;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.getAclChangeSet;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.getAclReaders;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.getNode;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.getNodeMetaData;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.getTransaction;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.indexAclChangeSet;
|
||||
import static org.alfresco.solr.utils.AlfrescoFileUtils.areDirectoryEquals;
|
||||
import static org.carrot2.shaded.guava.common.collect.ImmutableList.of;
|
||||
|
||||
/**
|
||||
* @author Elia Porciani
|
||||
*
|
||||
* This test check if the synchronization of contentstore between master and slave is done correctly.
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
public class ContentStoreReplicationIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
|
||||
protected static JettySolrRunner master;
|
||||
protected static JettySolrRunner slave;
|
||||
protected static SolrClient masterClient;
|
||||
protected static SolrClient slaveClient;
|
||||
|
||||
protected static Path masterSolrHome;
|
||||
protected static Path slaveSolrHome;
|
||||
|
||||
protected static Path masterContentStore;
|
||||
protected static Path slaveContentStore;
|
||||
|
||||
private static Acl acl;
|
||||
private static final int MILLIS_TIMOUT = 80000;
|
||||
|
||||
@BeforeClass
|
||||
public static void createMasterSlaveEnv() throws Exception
|
||||
{
|
||||
Properties properties = new Properties();
|
||||
|
||||
clientShards = new ArrayList<>();
|
||||
solrShards = new ArrayList<>();
|
||||
solrCollectionNameToStandaloneClient = new HashMap<>();
|
||||
jettyContainers = new HashMap<>();
|
||||
|
||||
String coreName = "master";
|
||||
boolean basicAuth = Boolean.parseBoolean(properties.getProperty("BasicAuth", "false"));
|
||||
|
||||
String masterDir = "master" + Time.now();
|
||||
String slaveDir = "slave" + Time.now();
|
||||
|
||||
String masterKey = masterDir + "/solrHome";
|
||||
String slaveKey = slaveDir + "/solrHome";
|
||||
|
||||
master = createJetty(masterKey, basicAuth);
|
||||
addCoreToJetty(masterKey, coreName, coreName, null);
|
||||
startJetty(master);
|
||||
|
||||
|
||||
String slaveCoreName = "slave";
|
||||
slave = createJetty(slaveKey, basicAuth);
|
||||
addCoreToJetty(slaveKey, slaveCoreName, slaveCoreName, null);
|
||||
setMasterUrl(slaveKey, slaveCoreName, master.getBaseUrl().toString() + "/master");
|
||||
|
||||
startJetty(slave);
|
||||
|
||||
String masterStr = buildUrl(master.getLocalPort()) + "/" + coreName;
|
||||
String slaveStr = buildUrl(slave.getLocalPort()) + "/" + slaveCoreName;
|
||||
|
||||
masterClient = createNewSolrClient(masterStr);
|
||||
slaveClient = createNewSolrClient(slaveStr);
|
||||
|
||||
masterSolrHome = testDir.toPath().resolve(masterKey);
|
||||
slaveSolrHome = testDir.toPath().resolve(slaveKey);
|
||||
masterContentStore = testDir.toPath().resolve(masterDir + "/contentstore");
|
||||
slaveContentStore = testDir.toPath().resolve(slaveDir + "/contentstore");
|
||||
|
||||
AclChangeSet aclChangeSet = getAclChangeSet(1);
|
||||
|
||||
acl = getAcl(aclChangeSet);
|
||||
AclReaders aclReaders = getAclReaders(aclChangeSet, acl, singletonList("joel"), singletonList("phil"), null);
|
||||
|
||||
indexAclChangeSet(aclChangeSet,
|
||||
of(acl),
|
||||
of(aclReaders));
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void cleanupMasterSlave() throws Exception
|
||||
{
|
||||
master.stop();
|
||||
slave.stop();
|
||||
FileUtils.forceDelete(new File(masterSolrHome.getParent().toUri()));
|
||||
FileUtils.forceDelete(new File(slaveSolrHome.getParent().toUri()));
|
||||
|
||||
SOLRAPIQueueClient.nodeMetaDataMap.clear();
|
||||
SOLRAPIQueueClient.transactionQueue.clear();
|
||||
SOLRAPIQueueClient.aclChangeSetQueue.clear();
|
||||
SOLRAPIQueueClient.aclReadersMap.clear();
|
||||
SOLRAPIQueueClient.aclMap.clear();
|
||||
SOLRAPIQueueClient.nodeMap.clear();
|
||||
SOLRAPIQueueClient.nodeContentMap.clear();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void contentStoreReplicationTest() throws Exception
|
||||
{
|
||||
// ADD 250 nodes and check they are replicated
|
||||
int numNodes = 250;
|
||||
Transaction bigTxn = getTransaction(0, numNodes);
|
||||
List<Node> nodes = new ArrayList<>();
|
||||
List<NodeMetaData> nodeMetaDatas = new ArrayList<>();
|
||||
for(int i = 0; i<numNodes; i++) {
|
||||
Node node = getNode(i, bigTxn, acl, Node.SolrApiNodeStatus.UPDATED);
|
||||
nodes.add(node);
|
||||
NodeMetaData nodeMetaData = getNodeMetaData(node, bigTxn, acl, "mike", null, false);
|
||||
node.setNodeRef(nodeMetaData.getNodeRef().toString());
|
||||
nodeMetaDatas.add(nodeMetaData);
|
||||
}
|
||||
|
||||
indexTransaction(bigTxn, nodes, nodeMetaDatas);
|
||||
|
||||
waitForDocCountCore(masterClient,
|
||||
luceneToSolrQuery(new TermQuery(new Term("content@s___t@{http://www.alfresco.org/model/content/1.0}content", "world"))),
|
||||
numNodes, MILLIS_TIMOUT, System.currentTimeMillis());
|
||||
|
||||
long filesInMasterContentStore = Files.walk(Paths.get(masterContentStore.toUri().resolve("_DEFAULT_")))
|
||||
.filter(Files::isRegularFile)
|
||||
.count();
|
||||
|
||||
Assert.assertEquals( "master contentStore should have " + numNodes + "files", numNodes, filesInMasterContentStore);
|
||||
assertTrue("slave content store is not in sync after timeout", waitForContentStoreSync(MILLIS_TIMOUT));
|
||||
|
||||
|
||||
// ADD other 10 nodes
|
||||
int numUpdates = 10;
|
||||
int totalNodes = numNodes + numUpdates;
|
||||
Transaction updateTx = getTransaction(0, numUpdates);
|
||||
|
||||
List<Node> updateNodes = new ArrayList<>();
|
||||
List<NodeMetaData> updateNodeMetaDatas = new ArrayList<>();
|
||||
for(int i = numNodes; i < totalNodes; i++) {
|
||||
Node node = getNode(i, updateTx, acl, Node.SolrApiNodeStatus.UPDATED);
|
||||
updateNodes.add(node);
|
||||
NodeMetaData nodeMetaData = getNodeMetaData(node, updateTx, acl, "mike", null, false);
|
||||
node.setNodeRef(nodeMetaData.getNodeRef().toString());
|
||||
updateNodeMetaDatas.add(nodeMetaData);
|
||||
}
|
||||
|
||||
indexTransaction(updateTx, updateNodes, updateNodeMetaDatas);
|
||||
|
||||
waitForDocCountCore(masterClient,
|
||||
luceneToSolrQuery(new TermQuery(new Term("content@s___t@{http://www.alfresco.org/model/content/1.0}content", "world"))),
|
||||
numNodes + numUpdates, MILLIS_TIMOUT, System.currentTimeMillis());
|
||||
|
||||
filesInMasterContentStore = Files.walk(Paths.get(masterContentStore.toUri().resolve("_DEFAULT_")))
|
||||
.filter(Files::isRegularFile)
|
||||
.count();
|
||||
|
||||
Assert.assertEquals( "master contentStore should have " + totalNodes + "files", totalNodes, filesInMasterContentStore);
|
||||
assertTrue("slave content store is not in sync after timeout", waitForContentStoreSync(MILLIS_TIMOUT));
|
||||
|
||||
|
||||
// DELETES 30 nodes
|
||||
int numDeletes = 30;
|
||||
Transaction deleteTx = getTransaction(numDeletes, 0);
|
||||
|
||||
totalNodes = numNodes + numUpdates - numDeletes;
|
||||
List<Node> deleteNodes = new ArrayList<>();
|
||||
List<NodeMetaData> deleteNodeMetaDatas = new ArrayList<>();
|
||||
|
||||
for(int i = 0; i<numDeletes; i++) {
|
||||
Node node = getNode(i, deleteTx, acl, Node.SolrApiNodeStatus.DELETED);
|
||||
deleteNodes.add(node);
|
||||
NodeMetaData nodeMetaData = getNodeMetaData(node, deleteTx, acl, "mike", null, false);
|
||||
node.setNodeRef(nodeMetaData.getNodeRef().toString());
|
||||
deleteNodeMetaDatas.add(nodeMetaData);
|
||||
}
|
||||
|
||||
|
||||
indexTransaction(deleteTx, deleteNodes, deleteNodeMetaDatas);
|
||||
|
||||
waitForDocCountCore(masterClient,
|
||||
luceneToSolrQuery(new TermQuery(new Term("content@s___t@{http://www.alfresco.org/model/content/1.0}content", "world"))),
|
||||
totalNodes, MILLIS_TIMOUT, System.currentTimeMillis());
|
||||
|
||||
filesInMasterContentStore = Files.walk(Paths.get(masterContentStore.toUri().resolve("_DEFAULT_")))
|
||||
.filter(Files::isRegularFile)
|
||||
.count();
|
||||
|
||||
Assert.assertEquals( "master contentStore should have " + totalNodes + "files", totalNodes, filesInMasterContentStore);
|
||||
assertTrue("slave content store is not in sync after timeout", waitForContentStoreSync(MILLIS_TIMOUT));
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static boolean waitForContentStoreSync(long waitMillis) throws InterruptedException
|
||||
{
|
||||
|
||||
long startMillis = System.currentTimeMillis();
|
||||
long timeout = startMillis + waitMillis;
|
||||
long increment = 1;
|
||||
|
||||
while(new Date().getTime() < timeout)
|
||||
{
|
||||
try{
|
||||
|
||||
if (areDirectoryEquals(masterContentStore, slaveContentStore, new String[]{"gz"}, true))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e){
|
||||
// do nothing
|
||||
}
|
||||
Thread.sleep(500 * increment);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void setMasterUrl(String jettyKey, String coreName, String masterUrl) throws IOException
|
||||
{
|
||||
Path jettySolrHome = testDir.toPath().resolve(jettyKey);
|
||||
Path coreHome = jettySolrHome.resolve(coreName);
|
||||
Path confDir = coreHome.resolve("conf");
|
||||
|
||||
Path solrConfigPath = confDir.resolve("solrconfig.xml");
|
||||
|
||||
String content = new String(Files.readAllBytes(solrConfigPath));
|
||||
content = content.replaceAll("\\{masterURL\\}", masterUrl);
|
||||
Files.write(solrConfigPath, content.getBytes());
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
package org.alfresco.solr.highlight;
|
||||
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
@@ -35,9 +35,9 @@ import org.junit.Test;
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class AlfrescoHighligherDistributedTest extends AbstractAlfrescoDistributedTest
|
||||
public class AlfrescoHighligherDistributedIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(AlfrescoHighligherDistributedTest.class);
|
||||
private static Log logger = LogFactory.getLog(AlfrescoHighligherDistributedIT.class);
|
||||
|
||||
@BeforeClass
|
||||
private static void initData() throws Throwable
|
||||
+4
-20
@@ -22,13 +22,9 @@ package org.alfresco.solr.highlight;
|
||||
import java.util.Map;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.alfresco.solr.AlfrescoCoreAdminHandler;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT;
|
||||
import org.alfresco.solr.client.*;
|
||||
import org.alfresco.solr.dataload.TestDataProvider;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.lucene.index.Term;
|
||||
@@ -37,39 +33,27 @@ import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.LegacyNumericRangeQuery;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.common.params.HighlightParams;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.apache.solr.response.BasicResultContext;
|
||||
import org.apache.solr.response.SolrQueryResponse;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
|
||||
import static com.google.common.collect.ImmutableMap.of;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.stream.IntStream.range;
|
||||
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.alfresco.model.ContentModel.PROP_CREATOR;
|
||||
import static org.alfresco.model.ContentModel.PROP_RATING_SCHEME;
|
||||
import static org.alfresco.model.ContentModel.TYPE_CONTENT;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.*;
|
||||
import static org.apache.solr.SolrJettyTestBase.jetty;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class AlfrescoHighlighterTest extends AbstractAlfrescoSolrTests
|
||||
public class AlfrescoHighlighterIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(AlfrescoHighlighterTest.class);
|
||||
private static Log logger = LogFactory.getLog(AlfrescoHighlighterIT.class);
|
||||
private static long MAX_WAIT_TIME = 80000;
|
||||
|
||||
@BeforeClass
|
||||
-176
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* 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.alfresco.solr.highlight;
|
||||
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.handler.component.AlfrescoSolrHighlighter;
|
||||
import org.apache.solr.handler.component.HighlightComponent;
|
||||
import org.apache.solr.highlight.PostingsSolrHighlighter;
|
||||
import org.apache.solr.highlight.SolrHighlighter;
|
||||
import org.apache.solr.schema.IndexSchema;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import static junit.framework.TestCase.*;
|
||||
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class TestPostingsSolrHighlighter extends AbstractAlfrescoSolrTests
|
||||
{
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception
|
||||
{
|
||||
initAlfrescoCore("schema.xml");
|
||||
// test our config is sane, just to be sure:
|
||||
|
||||
// postingshighlighter should be used
|
||||
SolrHighlighter highlighter = HighlightComponent.getHighlighter(getCore());
|
||||
assertTrue("wrong highlighter: " + highlighter.getClass(), highlighter instanceof AlfrescoSolrHighlighter);
|
||||
|
||||
// 'text' and 'text3' should have offsets, 'text2' should not
|
||||
IndexSchema schema = getCore().getLatestSchema();
|
||||
assertTrue(schema.getField("text").storeOffsetsWithPositions());
|
||||
assertTrue(schema.getField("text3").storeOffsetsWithPositions());
|
||||
assertFalse(schema.getField("text2").storeOffsetsWithPositions());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
|
||||
// if you override setUp or tearDown, you better call
|
||||
// the super classes version
|
||||
clearIndex();
|
||||
assertU(adoc("text", "document one", "text2", "document one", "text3", "crappy document", "id", "101"));
|
||||
assertU(adoc("text", "second document", "text2", "second document", "text3", "crappier document", "id", "102"));
|
||||
assertU(commit());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimple() {
|
||||
assertQ("simplest test",
|
||||
req("q", "text:document", "sort", "id asc", "hl", "true"),
|
||||
"count(//lst[@name='highlighting']/*)=2",
|
||||
"//lst[@name='highlighting']/lst[@name='101']/arr[@name='text']/str='<em>document</em> one'",
|
||||
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text']/str='second <em>document</em>'");
|
||||
}
|
||||
|
||||
public void testPagination() {
|
||||
assertQ("pagination test",
|
||||
req("q", "text:document", "sort", "id asc", "hl", "true", "rows", "1", "start", "1"),
|
||||
"count(//lst[@name='highlighting']/*)=1",
|
||||
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text']/str='second <em>document</em>'");
|
||||
}
|
||||
|
||||
public void testEmptySnippet() {
|
||||
assertQ("null snippet test",
|
||||
req("q", "text:one OR *:*", "sort", "id asc", "hl", "true"),
|
||||
"count(//lst[@name='highlighting']/*)=2",
|
||||
"//lst[@name='highlighting']/lst[@name='101']/arr[@name='text']/str='document <em>one</em>'",
|
||||
"count(//lst[@name='highlighting']/lst[@name='102']/arr[@name='text']/*)=0");
|
||||
}
|
||||
|
||||
public void testDefaultSummary() {
|
||||
assertQ("null snippet test",
|
||||
req("q", "text:one OR *:*", "sort", "id asc", "hl", "true", "hl.defaultSummary", "true"),
|
||||
"count(//lst[@name='highlighting']/*)=2",
|
||||
"//lst[@name='highlighting']/lst[@name='101']/arr[@name='text']/str='document <em>one</em>'",
|
||||
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text']/str='second document'");
|
||||
}
|
||||
|
||||
public void testDifferentField() {
|
||||
assertQ("highlighting text3",
|
||||
req("q", "text3:document", "sort", "id asc", "hl", "true", "hl.fl", "text3"),
|
||||
"count(//lst[@name='highlighting']/*)=2",
|
||||
"//lst[@name='highlighting']/lst[@name='101']/arr[@name='text3']/str='crappy <em>document</em>'",
|
||||
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text3']/str='crappier <em>document</em>'");
|
||||
}
|
||||
|
||||
public void testTwoFields() {
|
||||
assertQ("highlighting text and text3",
|
||||
req("q", "text:document text3:document", "sort", "id asc", "hl", "true", "hl.fl", "text,text3"),
|
||||
"count(//lst[@name='highlighting']/*)=2",
|
||||
"//lst[@name='highlighting']/lst[@name='101']/arr[@name='text']/str='<em>document</em> one'",
|
||||
"//lst[@name='highlighting']/lst[@name='101']/arr[@name='text3']/str='crappy <em>document</em>'",
|
||||
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text']/str='second <em>document</em>'",
|
||||
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text3']/str='crappier <em>document</em>'");
|
||||
}
|
||||
/**
|
||||
public void testMisconfiguredField() {
|
||||
ignoreException("was indexed without offsets");
|
||||
try {
|
||||
assertQ("should fail, has no offsets",
|
||||
req("q", "text2:document", "sort", "id asc", "hl", "true", "hl.fl", "text2"));
|
||||
fail();
|
||||
} catch (Exception expected) {
|
||||
// expected
|
||||
}
|
||||
resetExceptionIgnores();
|
||||
}
|
||||
**/
|
||||
public void testTags() {
|
||||
assertQ("different pre/post tags",
|
||||
req("q", "text:document", "sort", "id asc", "hl", "true", "hl.tag.pre", "[", "hl.tag.post", "]"),
|
||||
"count(//lst[@name='highlighting']/*)=2",
|
||||
"//lst[@name='highlighting']/lst[@name='101']/arr[@name='text']/str='[document] one'",
|
||||
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text']/str='second [document]'");
|
||||
}
|
||||
|
||||
public void testTagsPerField() {
|
||||
assertQ("highlighting text and text3",
|
||||
req("q", "text:document text3:document", "sort", "id asc", "hl", "true", "hl.fl", "text,text3", "f.text3.hl.tag.pre", "[", "f.text3.hl.tag.post", "]"),
|
||||
"count(//lst[@name='highlighting']/*)=2",
|
||||
"//lst[@name='highlighting']/lst[@name='101']/arr[@name='text']/str='<em>document</em> one'",
|
||||
"//lst[@name='highlighting']/lst[@name='101']/arr[@name='text3']/str='crappy [document]'",
|
||||
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text']/str='second <em>document</em>'",
|
||||
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text3']/str='crappier [document]'");
|
||||
}
|
||||
|
||||
public void testBreakIterator() {
|
||||
assertQ("different breakiterator",
|
||||
req("q", "text:document", "sort", "id asc", "hl", "true", "hl.bs.type", "WORD"),
|
||||
"count(//lst[@name='highlighting']/*)=2",
|
||||
"//lst[@name='highlighting']/lst[@name='101']/arr[@name='text']/str='<em>document</em>'",
|
||||
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text']/str='<em>document</em>'");
|
||||
}
|
||||
|
||||
public void testBreakIterator2() {
|
||||
assertU(adoc("text", "Document one has a first sentence. Document two has a second sentence.", "id", "103"));
|
||||
assertU(commit());
|
||||
assertQ("different breakiterator",
|
||||
req("q", "text:document", "sort", "id asc", "hl", "true", "hl.bs.type", "WHOLE"),
|
||||
"//lst[@name='highlighting']/lst[@name='103']/arr[@name='text']/str='<em>Document</em> one has a first sentence. <em>Document</em> two has a second sentence.'");
|
||||
}
|
||||
|
||||
public void testEncoder() {
|
||||
assertU(adoc("text", "Document one has a first <i>sentence</i>.", "id", "103"));
|
||||
assertU(commit());
|
||||
assertQ("html escaped",
|
||||
req("q", "text:document", "sort", "id asc", "hl", "true", "hl.encoder", "html"),
|
||||
"//lst[@name='highlighting']/lst[@name='103']/arr[@name='text']/str='<em>Document</em> one has a first <i>sentence</i>.'");
|
||||
}
|
||||
|
||||
public void testWildcard() {
|
||||
assertQ("simplest test",
|
||||
req("q", "text:doc*ment", "sort", "id asc", "hl", "true", "hl.highlightMultiTerm", "true"),
|
||||
"count(//lst[@name='highlighting']/*)=2",
|
||||
"//lst[@name='highlighting']/lst[@name='101']/arr[@name='text']/str='<em>document</em> one'",
|
||||
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text']/str='second <em>document</em>'");
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -22,7 +22,7 @@ package org.alfresco.solr.query;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
@@ -38,7 +38,7 @@ import org.junit.Test;
|
||||
|
||||
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class AlfrescoReRankQParserPluginTest extends AbstractAlfrescoSolrTests
|
||||
public class AlfrescoReRankQParserPluginIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception
|
||||
+2
-2
@@ -21,7 +21,7 @@ package org.alfresco.solr.query;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
import org.alfresco.solr.client.AclReaders;
|
||||
@@ -51,7 +51,7 @@ import static java.util.Collections.singletonList;
|
||||
import static java.util.stream.IntStream.range;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.*;
|
||||
|
||||
public class AlfrescoSolrFingerprintTest extends AbstractAlfrescoSolrTests
|
||||
public class AlfrescoSolrFingerprintIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
private static long MAX_WAIT_TIME = 80000;
|
||||
|
||||
+2
-16
@@ -19,31 +19,17 @@
|
||||
|
||||
package org.alfresco.solr.query;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.lucene.util.RefCount;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.response.ResultContext;
|
||||
import org.apache.solr.response.SolrQueryResponse;
|
||||
import org.apache.solr.search.DocIterator;
|
||||
import org.apache.solr.search.DocList;
|
||||
import org.apache.solr.search.SolrIndexSearcher;
|
||||
import org.apache.solr.util.RefCounted;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class AlfrescoSolrSpellcheckerTest extends AbstractAlfrescoSolrTests
|
||||
public class AlfrescoSolrSpellcheckerIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception
|
||||
+2
-2
@@ -34,7 +34,7 @@ import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT;
|
||||
import org.alfresco.solr.client.ContentPropertyValue;
|
||||
import org.alfresco.solr.client.MLTextPropertyValue;
|
||||
import org.alfresco.solr.client.PropertyValue;
|
||||
@@ -46,7 +46,7 @@ import org.junit.BeforeClass;
|
||||
* @author Michael Suzuki
|
||||
*
|
||||
*/
|
||||
public class AuthDataLoad extends AbstractAlfrescoSolrTests
|
||||
public class AuthDataLoad extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
static int count = 100;
|
||||
static long maxReader = 1000;
|
||||
|
||||
+1
-2
@@ -39,7 +39,7 @@ import org.junit.Test;
|
||||
* @author Michael Suzuki
|
||||
*
|
||||
*/
|
||||
public class AuthQueryTest extends AuthDataLoad
|
||||
public class AuthQueryIT extends AuthDataLoad
|
||||
{
|
||||
@Test
|
||||
public void checkAuth()
|
||||
@@ -109,7 +109,6 @@ public class AuthQueryTest extends AuthDataLoad
|
||||
searchParameters.setQuery(queryString);
|
||||
Query query = dataModel.getFTSQuery(new Pair<SearchParameters, Boolean>(searchParameters, Boolean.FALSE),
|
||||
solrQueryRequest, FTSQueryParser.RerankPhase.SINGLE_PASS);
|
||||
System.out.println("##################### Query:"+query);
|
||||
TopDocs docs = solrIndexSearcher.search(query, count * 2 + 10);
|
||||
|
||||
Assert.assertEquals(count, docs.totalHits);
|
||||
+3
-3
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
package org.alfresco.solr.query;
|
||||
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.client.solrj.response.FacetField;
|
||||
@@ -37,12 +37,12 @@ import static org.hamcrest.core.Is.is;
|
||||
|
||||
@SolrTestCaseJ4.SuppressSSL @LuceneTestCase.SuppressCodecs({ "Appending", "Lucene3x", "Lucene40", "Lucene41",
|
||||
"Lucene42", "Lucene43", "Lucene44", "Lucene45", "Lucene46", "Lucene47", "Lucene48",
|
||||
"Lucene49" }) public class DistributedAlfrescoSolrFacetingTest extends AbstractAlfrescoDistributedTest
|
||||
"Lucene49" }) public class DistributedAlfrescoSolrFacetingIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
@BeforeClass
|
||||
private static void initData() throws Throwable
|
||||
{
|
||||
initSolrServers(2, "DistributedAlfrescoSolrFacetingTest", null);
|
||||
initSolrServers(2, "DistributedAlfrescoSolrFacetingIT", null);
|
||||
indexSampleDocumentsForFacetingMincount();
|
||||
|
||||
}
|
||||
+2
-2
@@ -34,7 +34,7 @@ import java.util.Random;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
import org.alfresco.solr.client.AclReaders;
|
||||
@@ -61,7 +61,7 @@ import org.junit.Test;
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class DistributedAlfrescoSolrFingerPrintTest extends AbstractAlfrescoDistributedTest
|
||||
public class DistributedAlfrescoSolrFingerPrintIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
private static long MAX_WAIT_TIME = 80000;
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
package org.alfresco.solr.query;
|
||||
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
@@ -32,7 +32,7 @@ import org.junit.Test;
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class DistributedAlfrescoSolrSpellcheckerTest extends AbstractAlfrescoDistributedTest
|
||||
public class DistributedAlfrescoSolrSpellcheckerIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
@BeforeClass
|
||||
private static void initData() throws Throwable
|
||||
+2
-3
@@ -20,19 +20,18 @@
|
||||
package org.alfresco.solr.query;
|
||||
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
|
||||
public class SolrAuthTest extends AbstractAlfrescoSolrTests
|
||||
public class SolrAuthIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
|
||||
@BeforeClass
|
||||
+2
-4
@@ -18,9 +18,7 @@
|
||||
*/
|
||||
package org.alfresco.solr.query;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.service.cmr.search.GeneralHighlightParameters;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -32,7 +30,7 @@ import org.junit.Test;
|
||||
* @author Michael Suzuki
|
||||
*
|
||||
*/
|
||||
public class UntokenisedFieldTest extends AbstractAlfrescoSolrTests
|
||||
public class UntokenisedFieldIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
String nodeRef = "workspace://SpacesStore/00000000-0000-1-4731-76966678";
|
||||
String nodeRefS = "noderef@s_@mytest" ;
|
||||
+2
-11
@@ -18,25 +18,16 @@
|
||||
*/
|
||||
package org.alfresco.solr.query.afts.qparser;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.stream.IntStream.range;
|
||||
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
||||
/**
|
||||
* Supertype layer for all AFTS QParser tests.
|
||||
*
|
||||
* @author Andrea Gazzarini
|
||||
*/
|
||||
public abstract class AbstractQParserPluginTest extends AbstractAlfrescoSolrTests
|
||||
public abstract class AbstractQParserPluginIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
@BeforeClass
|
||||
public static void spinUpSolr() throws Exception
|
||||
+1
-1
@@ -24,7 +24,7 @@ import org.alfresco.util.ISO9075;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class FieldNameEscapingTest extends AbstractQParserPluginTest implements QueryConstants
|
||||
public class FieldNameEscapingIT extends AbstractQParserPluginIT implements QueryConstants
|
||||
{
|
||||
private static TestDataProvider DATASETS_PROVIDER;
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
public class QParserPluginTest extends AbstractQParserPluginTest implements QueryConstants
|
||||
public class QParserPluginIT extends AbstractQParserPluginIT implements QueryConstants
|
||||
{
|
||||
/** The UTC time zone. */
|
||||
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
|
||||
+1
-1
@@ -49,7 +49,7 @@ import static com.google.common.collect.ImmutableMap.of;
|
||||
* This test set checks that in all these query types the default fields are involved in the search.
|
||||
*
|
||||
*/
|
||||
public class AFTSDefaultTextQueryTest extends AbstractRequestHandlerTest
|
||||
public class AFTSDefaultTextQueryIT extends AbstractRequestHandlerIT
|
||||
{
|
||||
|
||||
@BeforeClass
|
||||
+1
-1
@@ -39,7 +39,7 @@ import org.junit.Test;
|
||||
*
|
||||
* @author msuzuki
|
||||
*/
|
||||
public class AFTSDisjunctionTest extends AbstractRequestHandlerTest
|
||||
public class AFTSDisjunctionIT extends AbstractRequestHandlerIT
|
||||
{
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception
|
||||
+1
-1
@@ -43,7 +43,7 @@ import java.util.Map;
|
||||
* @author eporciani
|
||||
* @author agazzarini
|
||||
*/
|
||||
public class AFTSIdentifierFieldsTest extends AbstractRequestHandlerTest
|
||||
public class AFTSIdentifierFieldsIT extends AbstractRequestHandlerIT
|
||||
{
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception
|
||||
+1
-1
@@ -41,7 +41,7 @@ import java.util.Map;
|
||||
*
|
||||
* @author elia
|
||||
*/
|
||||
public class AFTSRangeQueryTest extends AbstractRequestHandlerTest
|
||||
public class AFTSRangeQueryIT extends AbstractRequestHandlerIT
|
||||
{
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception
|
||||
+1
-1
@@ -20,7 +20,7 @@ import java.util.Locale;
|
||||
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
public class AFTSRequestHandlerTest extends AbstractRequestHandlerTest implements QueryConstants
|
||||
public class AFTSRequestHandlerIT extends AbstractRequestHandlerIT implements QueryConstants
|
||||
{
|
||||
private static TestDataProvider DATASETS_PROVIDER;
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package org.alfresco.solr.query.afts.requestHandler;
|
||||
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -12,7 +12,7 @@ import java.util.stream.Stream;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.stream.IntStream.range;
|
||||
|
||||
public abstract class AbstractRequestHandlerTest extends AbstractAlfrescoSolrTests
|
||||
public abstract class AbstractRequestHandlerIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
@BeforeClass
|
||||
public static void spinUpSolr() throws Exception
|
||||
+1
-1
@@ -9,7 +9,7 @@ import org.junit.Test;
|
||||
*
|
||||
* @author Andrea Gazzarini
|
||||
*/
|
||||
public class MNTTest extends AbstractRequestHandlerTest
|
||||
public class MNTIT extends AbstractRequestHandlerIT
|
||||
{
|
||||
@BeforeClass
|
||||
public static void loadData() throws Exception
|
||||
+1
-1
@@ -27,7 +27,7 @@ import org.junit.Test;
|
||||
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
public class AlfrescoCMISQParserPluginTest extends LoadCMISData implements QueryConstants
|
||||
public class AlfrescoCMISQParserPluginIT extends LoadCMISData implements QueryConstants
|
||||
{
|
||||
@Test
|
||||
public void cmisBasic() throws Exception
|
||||
+1
-1
@@ -31,7 +31,7 @@ import org.junit.Test;
|
||||
* @author Michael Suzuki
|
||||
*
|
||||
*/
|
||||
public class CmisTest extends LoadCMISData
|
||||
public class CmisIT extends LoadCMISData
|
||||
{
|
||||
@Before
|
||||
public void setup() throws Exception
|
||||
+2
-2
@@ -37,7 +37,7 @@ import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT;
|
||||
import org.alfresco.solr.AlfrescoSolrDataModel;
|
||||
import org.alfresco.solr.client.ContentPropertyValue;
|
||||
import org.alfresco.solr.client.MLTextPropertyValue;
|
||||
@@ -51,7 +51,7 @@ import org.junit.BeforeClass;
|
||||
* @author Michael Suzuki
|
||||
*
|
||||
*/
|
||||
public class LoadCMISData extends AbstractAlfrescoSolrTests
|
||||
public class LoadCMISData extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
protected static NodeRef testCMISContent00NodeRef;
|
||||
protected static NodeRef testCMISRootNodeRef;
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AclModCountRouterTest
|
||||
public class AclModCountRouterIT
|
||||
{
|
||||
private DocRouter router;
|
||||
|
||||
+3
-3
@@ -36,7 +36,7 @@ import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
import org.alfresco.solr.client.AclReaders;
|
||||
@@ -62,9 +62,9 @@ import org.junit.Test;
|
||||
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
public class AlfrescoSolrTrackerExceptionTest extends AbstractAlfrescoSolrTests
|
||||
public class AlfrescoSolrTrackerExceptionIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(AlfrescoSolrTrackerTest.class);
|
||||
private static Log logger = LogFactory.getLog(AlfrescoSolrTrackerExceptionIT.class);
|
||||
private static long MAX_WAIT_TIME = 80000;
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception
|
||||
+3
-3
@@ -37,7 +37,7 @@ import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
import org.alfresco.solr.client.AclReaders;
|
||||
@@ -64,9 +64,9 @@ import org.junit.Test;
|
||||
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
public class AlfrescoSolrTrackerTest extends AbstractAlfrescoSolrTests
|
||||
public class AlfrescoSolrTrackerIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(AlfrescoSolrTrackerTest.class);
|
||||
private static Log logger = LogFactory.getLog(AlfrescoSolrTrackerIT.class);
|
||||
private static long MAX_WAIT_TIME = 80000;
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception
|
||||
+3
-3
@@ -32,7 +32,7 @@ import java.util.Collection;
|
||||
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT;
|
||||
import org.alfresco.solr.AlfrescoCoreAdminHandler;
|
||||
|
||||
import org.alfresco.solr.client.Acl;
|
||||
@@ -58,9 +58,9 @@ import org.junit.Test;
|
||||
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
public class AlfrescoSolrTrackerRollbackTest extends AbstractAlfrescoSolrTests
|
||||
public class AlfrescoSolrTrackerRollbackIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(AlfrescoSolrTrackerTest.class);
|
||||
private static Log logger = LogFactory.getLog(AlfrescoSolrTrackerIT.class);
|
||||
private static long MAX_WAIT_TIME = 80000;
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception
|
||||
+2
-2
@@ -36,7 +36,7 @@ import static org.junit.Assert.assertNotEquals;
|
||||
|
||||
import org.alfresco.repo.index.shard.ShardState;
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT;
|
||||
import org.alfresco.solr.AlfrescoCoreAdminHandler;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
@@ -70,7 +70,7 @@ import java.util.stream.Collectors;
|
||||
*
|
||||
* @author agazzarini
|
||||
*/
|
||||
public class AlfrescoSolrTrackerStateTest extends AbstractAlfrescoSolrTests
|
||||
public class AlfrescoSolrTrackerStateIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception
|
||||
+2
-2
@@ -35,7 +35,7 @@ import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrTests;
|
||||
import org.alfresco.solr.AbstractAlfrescoSolrIT;
|
||||
import org.alfresco.solr.client.*;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.*;
|
||||
@@ -52,7 +52,7 @@ import java.util.List;
|
||||
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
public class CascadeTrackerTest extends AbstractAlfrescoSolrTests
|
||||
public class CascadeTrackerIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
private static long MAX_WAIT_TIME = 80000;
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ import org.mockito.Spy;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ContentTrackerTest
|
||||
public class ContentTrackerIT
|
||||
{
|
||||
private ContentTracker contentTracker;
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ import java.util.Properties;
|
||||
import java.util.Random;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DateMonthRouterTest
|
||||
public class DateMonthRouterIT
|
||||
{
|
||||
private Random randomizer = new Random();
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import org.alfresco.repo.index.shard.ShardMethodEnum;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.SolrInformationServer;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
@@ -57,7 +57,7 @@ import static org.alfresco.solr.AlfrescoSolrUtils.list;
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class DistributedAclIdAlfrescoSolrTrackerTest extends AbstractAlfrescoDistributedTest
|
||||
public class DistributedAclIdAlfrescoSolrTrackerIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
|
||||
@BeforeClass
|
||||
+2
-2
@@ -19,7 +19,7 @@
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
import org.alfresco.solr.client.Node;
|
||||
@@ -50,7 +50,7 @@ import static org.alfresco.solr.AlfrescoSolrUtils.getTransaction;
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class DistributedAlfrescoSolrJsonTest extends AbstractAlfrescoDistributedTest
|
||||
public class DistributedAlfrescoSolrJsonIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
@BeforeClass
|
||||
private static void initData() throws Throwable
|
||||
+2
-2
@@ -19,7 +19,7 @@
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
import org.alfresco.solr.client.AclReaders;
|
||||
@@ -56,7 +56,7 @@ import static org.alfresco.solr.AlfrescoSolrUtils.list;
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class DistributedAlfrescoSolrTrackerTest extends AbstractAlfrescoDistributedTest
|
||||
public class DistributedAlfrescoSolrTrackerIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
|
||||
@BeforeClass
|
||||
+2
-2
@@ -19,7 +19,7 @@
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
import org.alfresco.solr.client.AclReaders;
|
||||
@@ -54,7 +54,7 @@ import static org.alfresco.solr.AlfrescoSolrUtils.indexAclChangeSet;
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class DistributedAlfrescoSolrTrackerRaceTest extends AbstractAlfrescoDistributedTest
|
||||
public class DistributedAlfrescoSolrTrackerRaceIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
|
||||
@BeforeClass
|
||||
+2
-2
@@ -33,7 +33,7 @@ import static org.alfresco.solr.AlfrescoSolrUtils.indexAclChangeSet;
|
||||
|
||||
import org.alfresco.repo.index.shard.ShardState;
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.AlfrescoCoreAdminHandler;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
@@ -68,7 +68,7 @@ import java.util.stream.Collectors;
|
||||
* @author agazzarini
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
public class DistributedAlfrescoSolrTrackerStateTest extends AbstractAlfrescoDistributedTest
|
||||
public class DistributedAlfrescoSolrTrackerStateIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
@BeforeClass
|
||||
private static void initData() throws Throwable
|
||||
+2
-2
@@ -30,7 +30,7 @@ import static org.alfresco.solr.AlfrescoSolrUtils.indexAclChangeSet;
|
||||
import static org.carrot2.shaded.guava.common.collect.ImmutableList.of;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
import org.alfresco.solr.client.AclReaders;
|
||||
@@ -55,7 +55,7 @@ import java.util.Properties;
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class DistributedCascadeTrackerTest extends AbstractAlfrescoDistributedTest
|
||||
public class DistributedCascadeTrackerIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
private Node parentFolder;
|
||||
private NodeMetaData parentFolderMetadata;
|
||||
+2
-2
@@ -21,7 +21,7 @@ package org.alfresco.solr.tracker;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.AlfrescoSolrDataModel;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
@@ -63,7 +63,7 @@ import static org.alfresco.solr.AlfrescoSolrUtils.indexAclChangeSet;
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public abstract class DistributedDateAbstractSolrTrackerTest extends AbstractAlfrescoDistributedTest
|
||||
public abstract class DistributedDateAbstractSolrTrackerIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
@Test
|
||||
public void testDateMonth() throws Exception
|
||||
+3
-3
@@ -33,7 +33,7 @@ import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.index.shard.ShardMethodEnum;
|
||||
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.AlfrescoSolrDataModel;
|
||||
import org.alfresco.solr.SolrInformationServer;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
@@ -63,13 +63,13 @@ import java.util.Properties;
|
||||
import java.util.TimeZone;
|
||||
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
public class DistributedDateMonthAlfrescoSolrTrackerTest extends AbstractAlfrescoDistributedTest
|
||||
public class DistributedDateMonthAlfrescoSolrTrackerIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
@BeforeClass
|
||||
@SuppressWarnings("unused")
|
||||
public static void initData() throws Throwable
|
||||
{
|
||||
initSolrServers(5, DistributedDateMonthAlfrescoSolrTrackerTest.class.getSimpleName(), getShardMethod());
|
||||
initSolrServers(5, DistributedDateMonthAlfrescoSolrTrackerIT.class.getSimpleName(), getShardMethod());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
+1
-1
@@ -33,7 +33,7 @@ import java.util.Properties;
|
||||
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class DistributedDateQuarterAlfrescoSolrTrackerTest extends DistributedDateAbstractSolrTrackerTest
|
||||
public class DistributedDateQuarterAlfrescoSolrTrackerIT extends DistributedDateAbstractSolrTrackerIT
|
||||
{
|
||||
|
||||
@BeforeClass
|
||||
+2
-2
@@ -33,12 +33,12 @@ import java.util.Properties;
|
||||
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class DistributedDateSplitYearAlfrescoSolrTrackerTest extends DistributedDateAbstractSolrTrackerTest
|
||||
public class DistributedDateSplitYearAlfrescoSolrTrackerIT extends DistributedDateAbstractSolrTrackerIT
|
||||
{
|
||||
@BeforeClass
|
||||
private static void initData() throws Throwable
|
||||
{
|
||||
initSolrServers(3, "DistributedDateSplitYearAlfrescoSolrTrackerTest", getShardMethod());
|
||||
initSolrServers(3, "DistributedDateSplitYearAlfrescoSolrTrackerIT", getShardMethod());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
+3
-3
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.SolrInformationServer;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
@@ -53,12 +53,12 @@ import static org.alfresco.solr.AlfrescoSolrUtils.list;
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class DistributedDbidRangeAlfrescoSolrTrackerTest extends AbstractAlfrescoDistributedTest
|
||||
public class DistributedDbidRangeAlfrescoSolrTrackerIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
@BeforeClass
|
||||
private static void initData() throws Throwable
|
||||
{
|
||||
initSolrServers(2, "DistributedDbidRangeAlfrescoSolrTrackerTest", getShardMethod());
|
||||
initSolrServers(2, "DistributedDbidRangeAlfrescoSolrTrackerIT", getShardMethod());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
+3
-3
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.SolrInformationServer;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
@@ -56,12 +56,12 @@ import static org.alfresco.solr.AlfrescoSolrUtils.list;
|
||||
*/
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class DistributedExpandDbidRangeAlfrescoSolrTrackerTest extends AbstractAlfrescoDistributedTest
|
||||
public class DistributedExpandDbidRangeAlfrescoSolrTrackerIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
@BeforeClass
|
||||
private static void initData() throws Throwable
|
||||
{
|
||||
initSolrServers(2, "DistributedExpandDbidRangeAlfrescoSolrTrackerTest", getShardMethod());
|
||||
initSolrServers(2, "DistributedExpandDbidRangeAlfrescoSolrTrackerIT", getShardMethod());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
+2
-2
@@ -21,7 +21,7 @@ package org.alfresco.solr.tracker;
|
||||
import java.util.Properties;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.index.shard.ShardMethodEnum;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
import org.alfresco.solr.client.AclReaders;
|
||||
@@ -53,7 +53,7 @@ import static org.carrot2.shaded.guava.common.collect.ImmutableList.of;
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@SolrTestCaseJ4.SuppressObjectReleaseTracker (bugUrl = "RAMDirectory")
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class DistributedExplicitShardIdWithStaticPropertyRouterTest extends AbstractAlfrescoDistributedTest
|
||||
public class DistributedExplicitShardIdWithStaticPropertyRouterIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
private static long MAX_WAIT_TIME = 80000;
|
||||
private final int timeout = 100000;
|
||||
+3
-3
@@ -20,7 +20,7 @@ package org.alfresco.solr.tracker;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.index.shard.ShardMethodEnum;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.SolrInformationServer;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
@@ -62,12 +62,12 @@ import static org.alfresco.solr.tracker.DocRouterFactory.SHARD_KEY_KEY;
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@SolrTestCaseJ4.SuppressObjectReleaseTracker (bugUrl = "RAMDirectory")
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class DistributedExplicitShardRoutingTrackerTest extends AbstractAlfrescoDistributedTest
|
||||
public class DistributedExplicitShardRoutingTrackerIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
@BeforeClass
|
||||
private static void initData() throws Throwable
|
||||
{
|
||||
initSolrServers(3, "DistributedExplicitShardRoutingTrackerTest", getProperties());
|
||||
initSolrServers(3, "DistributedExplicitShardRoutingTrackerIT", getProperties());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
+3
-3
@@ -41,7 +41,7 @@ import com.carrotsearch.randomizedtesting.RandomizedContext;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.index.shard.ShardMethodEnum;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedTest;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.SolrInformationServer;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
import org.alfresco.solr.client.AclChangeSet;
|
||||
@@ -66,7 +66,7 @@ import org.junit.Test;
|
||||
@SolrTestCaseJ4.SuppressSSL
|
||||
@SolrTestCaseJ4.SuppressObjectReleaseTracker (bugUrl = "RAMDirectory")
|
||||
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
|
||||
public class DistributedPropertyBasedAlfrescoSolrTrackerTest extends AbstractAlfrescoDistributedTest
|
||||
public class DistributedPropertyBasedAlfrescoSolrTrackerIT extends AbstractAlfrescoDistributedIT
|
||||
{
|
||||
private static final String[] DOMAINS = {"alfresco.com", "king.com", "gmail.com", "yahoo.com", "cookie.es"};
|
||||
private static final Map<String,Integer> domainsCount = new HashMap<>();
|
||||
@@ -78,7 +78,7 @@ public class DistributedPropertyBasedAlfrescoSolrTrackerTest extends AbstractAlf
|
||||
{
|
||||
domainsCount.put(domain,0);
|
||||
}
|
||||
initSolrServers(4, "DistributedPropertyBasedAlfrescoSolrTrackerTest", getProperties());
|
||||
initSolrServers(4, "DistributedPropertyBasedAlfrescoSolrTrackerIT", getProperties());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
+1
-1
@@ -41,7 +41,7 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ExplicitIDRouterTest
|
||||
public class ExplicitIDRouterIT
|
||||
{
|
||||
private DocRouter router;
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ExplicitIDWithLRISRouterTest
|
||||
public class ExplicitIDWithLRISRouterIT
|
||||
{
|
||||
private DocRouter router;
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ModelTrackerTest
|
||||
public class ModelTrackerIT
|
||||
{
|
||||
private ModelTracker modelTracker;
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PropertyRouterTest
|
||||
public class PropertyRouterIT
|
||||
{
|
||||
private PropertyRouter router;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user