diff --git a/config/alfresco/subsystems/Search/solr4/solr-search-context.xml b/config/alfresco/subsystems/Search/solr4/solr-search-context.xml
index 64b16fd590..049e1d0d9e 100644
--- a/config/alfresco/subsystems/Search/solr4/solr-search-context.xml
+++ b/config/alfresco/subsystems/Search/solr4/solr-search-context.xml
@@ -90,6 +90,15 @@
SpacesStore
+
+ ${solr4.alfresco.nodeString}
+
+
+ ${solr4.alfresco.numShards}
+
+
+ ${solr4.alfresco.replicationFactor}
+
@@ -107,6 +116,15 @@
SpacesStore
+
+ ${solr4.archive.nodeString}
+
+
+ ${solr4.archive.numShards}
+
+
+ ${solr4.archive.replicationFactor}
+
diff --git a/config/alfresco/subsystems/Search/solr4/solr-search.properties b/config/alfresco/subsystems/Search/solr4/solr-search.properties
index 9c9a799611..857f822afb 100644
--- a/config/alfresco/subsystems/Search/solr4/solr-search.properties
+++ b/config/alfresco/subsystems/Search/solr4/solr-search.properties
@@ -7,4 +7,15 @@ solr.baseUrl=/solr4
#
# Solr Suggester properties
#
-solr.suggester.enabled=true
\ No newline at end of file
+solr.suggester.enabled=true
+#
+# Default unsharded
+
+solr4.alfresco.nodeString=
+solr4.alfresco.numShards=1
+solr4.alfresco.replicationFactor=1
+
+solr4.archive.nodeString=
+solr4.archive.numShards=1
+solr4.archive.replicationFactor=1
+
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java
index 9d62c0f20a..9e76664358 100644
--- a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java
+++ b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2011 Alfresco Software Limited.
+ * Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -59,6 +59,7 @@ import org.alfresco.service.cmr.search.SearchParameters.SortDefinition;
import org.alfresco.service.cmr.search.StatsParameters;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService;
+import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.codec.net.URLCodec;
import org.apache.commons.httpclient.Header;
@@ -104,9 +105,7 @@ public class SolrQueryHTTPClient implements BeanFactoryAware
private List storeMappings;
- private HashMap httpClients = new HashMap();
-
- private HashMap> mappingLookup = new HashMap>();
+ private HashMap mappingLookup = new HashMap();
private String alternativeDictionary = CMISStrictDictionaryService.DEFAULT;
@@ -137,21 +136,7 @@ public class SolrQueryHTTPClient implements BeanFactoryAware
for(SolrStoreMapping mapping : storeMappings)
{
- List storeMappingList = mappingLookup.get(mapping.getStoreRef());
- if(storeMappingList == null)
- {
- storeMappingList = new ArrayList(50);
- mappingLookup.put(mapping.getStoreRef(), storeMappingList);
-
- HttpClientFactory httpClientFactory = (HttpClientFactory)beanFactory.getBean(mapping.getHttpClientFactory());
- HttpClient httpClient = httpClientFactory.getHttpClient();
- HttpClientParams params = httpClient.getParams();
- params.setBooleanParameter(HttpClientParams.PREEMPTIVE_AUTHENTICATION, true);
- httpClient.getState().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), new UsernamePasswordCredentials("admin", "admin"));
- httpClients.put(mapping.getStoreRef(), httpClient);
- }
-
- storeMappingList.add(mapping);
+ mappingLookup.put(mapping.getStoreRef(), new SolrStoreMappingWrapper(mapping, beanFactory));
}
}
@@ -245,13 +230,20 @@ public class SolrQueryHTTPClient implements BeanFactoryAware
try
{
StoreRef store = extractStoreRef(searchParameters);
- List mappings = extractMappings(store);
+ SolrStoreMappingWrapper mapping = extractMapping(store);
Locale locale = extractLocale(searchParameters);
- String url = buildStatsUrl(searchParameters, mappings.get(0).getBaseUrl(), locale);
+ Pair httpClientAndBaseUrl = mapping.getHttpClientAndBaseUrl();
+ HttpClient httpClient = httpClientAndBaseUrl.getFirst();
+ String url = buildStatsUrl(searchParameters, httpClientAndBaseUrl.getSecond(), locale);
JSONObject body = buildStatsBody(searchParameters, tenantService.getCurrentUserDomain(), locale);
- return (SolrStatsResult) postSolrQuery(store, url, body, new SolrJsonProcessor() {
+ if(httpClient == null)
+ {
+ throw new AlfrescoRuntimeException("No http client for store " + store.toString());
+ }
+
+ return (SolrStatsResult) postSolrQuery(httpClient, url, body, new SolrJsonProcessor() {
@Override
public SolrStatsResult getResult(JSONObject json)
@@ -335,12 +327,16 @@ public class SolrQueryHTTPClient implements BeanFactoryAware
try
{
StoreRef store = extractStoreRef(searchParameters);
- List mappings = extractMappings(store);
+ SolrStoreMappingWrapper mapping = extractMapping(store);
Locale locale = extractLocale(searchParameters);
URLCodec encoder = new URLCodec();
StringBuilder url = new StringBuilder();
- url.append(mappings.get(0).getBaseUrl());
+
+ Pair httpClientAndBaseUrl = mapping.getHttpClientAndBaseUrl();
+ HttpClient httpClient = httpClientAndBaseUrl.getFirst();
+
+ url.append(httpClientAndBaseUrl.getSecond());
String languageUrlFragment = extractLanguageFragment(language);
url.append("/").append(languageUrlFragment);
@@ -351,29 +347,25 @@ public class SolrQueryHTTPClient implements BeanFactoryAware
url.append("?wt=").append(encoder.encode("json", "UTF-8"));
url.append("&fl=").append(encoder.encode("DBID,score", "UTF-8"));
- if((searchParameters.getStores().size() > 1) || (mappings.size() > 1))
+ if((searchParameters.getStores().size() > 1) || (mapping.isSharded()))
{
boolean requiresSeparator = false;
url.append("&shards=");
for(StoreRef storeRef : searchParameters.getStores())
{
- for(SolrStoreMapping shard : extractMappings(storeRef))
+ SolrStoreMappingWrapper storeMapping = extractMapping(storeRef);
+
+ if(requiresSeparator)
{
- if(requiresSeparator)
- {
- url.append(',');
- }
- else
- {
- requiresSeparator = true;
- }
-
- HttpClientFactory httpClientFactory = (HttpClientFactory)beanFactory.getBean(shard.getHttpClientFactory());
- url.append(httpClientFactory.getHost());
- url.append(':');
- url.append(httpClientFactory.getPort());
- url.append(encoder.encode(shard.getBaseUrl(), "UTF-8"));
+ url.append(',');
}
+ else
+ {
+ requiresSeparator = true;
+ }
+
+ url.append(storeMapping.getShards());
+
}
}
@@ -551,7 +543,10 @@ public class SolrQueryHTTPClient implements BeanFactoryAware
body.put("textAttributes", textAttributes);
final int maximumResults = maxResults; //just needed for the final parameter
- return (ResultSet) postSolrQuery(store, url.toString(), body, new SolrJsonProcessor() {
+
+
+
+ return (ResultSet) postSolrQuery(httpClient, url.toString(), body, new SolrJsonProcessor() {
@Override
public SolrJSONResultSet getResult(JSONObject json)
@@ -579,24 +574,24 @@ public class SolrQueryHTTPClient implements BeanFactoryAware
}
}
- protected JSONResult postSolrQuery(StoreRef store, String url, JSONObject body, SolrJsonProcessor> jsonProcessor)
+ protected JSONResult postSolrQuery(HttpClient httpClient, String url, JSONObject body, SolrJsonProcessor> jsonProcessor)
throws UnsupportedEncodingException, IOException, HttpException, URIException,
JSONException
{
- return postSolrQuery(store, url, body, jsonProcessor, null);
+ return postSolrQuery(httpClient, url, body, jsonProcessor, null);
}
- protected JSONResult postSolrQuery(StoreRef store, String url, JSONObject body, SolrJsonProcessor> jsonProcessor, String spellCheckParams)
+ protected JSONResult postSolrQuery(HttpClient httpClient, String url, JSONObject body, SolrJsonProcessor> jsonProcessor, String spellCheckParams)
throws UnsupportedEncodingException, IOException, HttpException, URIException,
JSONException
{
- JSONObject json = postQuery(store, url, body);
+ JSONObject json = postQuery(httpClient, url, body);
if (spellCheckParams != null)
{
SpellCheckDecisionManager manager = new SpellCheckDecisionManager(json, url, body, spellCheckParams);
if (manager.isCollate())
{
- json = postQuery(store, manager.getUrl(), body);
+ json = postQuery(httpClient, manager.getUrl(), body);
}
json.put("spellcheck", manager.getSpellCheckJsonValue());
}
@@ -613,7 +608,7 @@ public class SolrQueryHTTPClient implements BeanFactoryAware
return results;
}
- protected JSONObject postQuery(StoreRef store, String url, JSONObject body) throws UnsupportedEncodingException,
+ protected JSONObject postQuery(HttpClient httpClient, String url, JSONObject body) throws UnsupportedEncodingException,
IOException, HttpException, URIException, JSONException
{
PostMethod post = new PostMethod(url);
@@ -625,13 +620,6 @@ public class SolrQueryHTTPClient implements BeanFactoryAware
try
{
- HttpClient httpClient = httpClients.get(store);
-
- if(httpClient == null)
- {
- throw new AlfrescoRuntimeException("No http client for store " + store.toString());
- }
-
httpClient.executeMethod(post);
if(post.getStatusCode() == HttpStatus.SC_MOVED_PERMANENTLY || post.getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY)
@@ -719,11 +707,11 @@ public class SolrQueryHTTPClient implements BeanFactoryAware
return languageUrlFragment;
}
- private List extractMappings(StoreRef store)
+ private SolrStoreMappingWrapper extractMapping(StoreRef store)
{
- List mappings = mappingLookup.get(store);
+ SolrStoreMappingWrapper mappings = mappingLookup.get(store);
- if ((mappings == null) || (mappings.size() == 0))
+ if (mappings == null)
{
throw new AlfrescoRuntimeException("No solr query support for store " + store);
}
diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrStoreMapping.java b/source/java/org/alfresco/repo/search/impl/solr/SolrStoreMapping.java
index 911408afaa..a5795e25df 100644
--- a/source/java/org/alfresco/repo/search/impl/solr/SolrStoreMapping.java
+++ b/source/java/org/alfresco/repo/search/impl/solr/SolrStoreMapping.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2010 Alfresco Software Limited.
+ * Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -18,10 +18,10 @@
*/
package org.alfresco.repo.search.impl.solr;
+import java.util.LinkedHashSet;
+
import org.alfresco.service.cmr.repository.StoreRef;
-import org.alfresco.util.PropertyCheck;
import org.springframework.beans.factory.BeanNameAware;
-import org.springframework.beans.factory.InitializingBean;
/**
* @author Andy
@@ -41,18 +41,24 @@ public class SolrStoreMapping implements BeanNameAware
private String beanName;
+ private String[] nodes = new String[0];
+
+ private int numShards = 1;
+
+ private int replicationFactor = 1;
+
public SolrStoreMapping()
{
}
- public SolrStoreMapping(String protocol, String identifier, String httpClientFactory, String baseUrl)
- {
- this.protocol = protocol;
- this.identifier = identifier;
- this.httpClientFactory = httpClientFactory;
- this.baseUrl = baseUrl;
- }
+// public SolrStoreMapping(String protocol, String identifier, String httpClientFactory, String baseUrl)
+// {
+// this.protocol = protocol;
+// this.identifier = identifier;
+// this.httpClientFactory = httpClientFactory;
+// this.baseUrl = baseUrl;
+// }
/**
* @return the storeRef
@@ -146,5 +152,94 @@ public class SolrStoreMapping implements BeanNameAware
}
}
+ /**
+ * @return the nodes
+ */
+ public String getNodeString()
+ {
+ StringBuilder builder = new StringBuilder();
+ for(String node : nodes)
+ {
+ if(builder.length() > 0)
+ {
+ builder.append(',');
+ }
+ builder.append(node);
+ }
+ return builder.toString();
+ }
+ /**
+ * @return the nodes
+ */
+ public String[] getNodes()
+ {
+ return nodes;
+ }
+
+ /**
+ * @param nodes
+ * the nodes to set
+ */
+// public void setNodes(String[] nodes)
+// {
+// LinkedHashSet unique = new LinkedHashSet();
+// for(String node : nodes)
+// {
+// for(String split : node.split(","))
+// {
+// unique.add(split.trim());
+// }
+// }
+//
+// this.nodes = unique.toArray(new String[0]);
+// }
+
+ public void setNodeString(String nodes)
+ {
+ LinkedHashSet unique = new LinkedHashSet();
+
+ for(String split : nodes.split(","))
+ {
+ unique.add(split.trim());
+ }
+
+
+ this.nodes = unique.toArray(new String[0]);
+ }
+
+ /**
+ * @return the numShards
+ */
+ public int getNumShards()
+ {
+ return numShards;
+ }
+
+ /**
+ * @param numShards
+ * the numShards to set
+ */
+ public void setNumShards(int numShards)
+ {
+ this.numShards = numShards;
+ }
+
+ /**
+ * @return the replicationFactor
+ */
+ public int getReplicationFactor()
+ {
+ return replicationFactor;
+ }
+
+ /**
+ * @param replicationFactor
+ * the replicationFactor to set
+ */
+ public void setReplicationFactor(int replicationFactor)
+ {
+ this.replicationFactor = replicationFactor;
+ }
+
}
diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrStoreMappingWrapper.java b/source/java/org/alfresco/repo/search/impl/solr/SolrStoreMappingWrapper.java
new file mode 100644
index 0000000000..ea71b69a0a
--- /dev/null
+++ b/source/java/org/alfresco/repo/search/impl/solr/SolrStoreMappingWrapper.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2005-2015 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.repo.search.impl.solr;
+
+import java.io.UnsupportedEncodingException;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.httpclient.HttpClientFactory;
+import org.alfresco.repo.search.impl.lucene.LuceneQueryParserException;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.util.Pair;
+import org.alfresco.util.shard.ExplicitShardingPolicy;
+import org.apache.commons.codec.net.URLCodec;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.params.HttpClientParams;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.BeanNameAware;
+
+/**
+ * @author Andy
+ */
+public class SolrStoreMappingWrapper
+{
+
+ private HttpClientFactory httpClientFactory;
+
+ private LinkedHashSet httpClientsAndBaseURLs = new LinkedHashSet();
+
+ private ExplicitShardingPolicy policy;
+
+ private Random random;
+
+ private BeanFactory beanFactory;
+
+ private SolrStoreMapping wrapped;
+
+ public SolrStoreMappingWrapper(SolrStoreMapping wrapped, BeanFactory beanFactory)
+ {
+ this.wrapped = wrapped;
+ this.beanFactory = beanFactory;
+ init();
+ }
+
+ public void init()
+ {
+ httpClientFactory = (HttpClientFactory)beanFactory.getBean(wrapped.getHttpClientFactory());
+ random = new Random(123);
+
+ if ((wrapped.getNodes() == null) || (wrapped.getNodes().length == 0))
+ {
+ HttpClient httpClient = httpClientFactory.getHttpClient();
+ HttpClientParams params = httpClient.getParams();
+ //params.setBooleanParameter(HttpClientParams.PREEMPTIVE_AUTHENTICATION, true);
+ //httpClient.getState().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), new UsernamePasswordCredentials("admin", "admin"));
+ httpClientsAndBaseURLs.add(new HttpClientAndBaseUrl(httpClient, wrapped.getBaseUrl()));
+ }
+ else
+ {
+ for (String node : wrapped.getNodes())
+ {
+ String nodeHost = httpClientFactory.getHost();
+ String nodePort = "" + httpClientFactory.getPort();
+ String nodeBaseUrl = wrapped.getBaseUrl();
+
+ if (node.length() > 0)
+ {
+ int colon = node.indexOf(':');
+ int forward = (colon > -1) ? node.indexOf('/', colon) : node.indexOf('/');
+
+ if (colon == -1)
+ {
+ if (forward == -1)
+ {
+ // single value
+ if (node.startsWith("/"))
+ {
+ nodeBaseUrl = node;
+ }
+ try
+ {
+ int port = Integer.parseInt(node);
+ nodePort = "" + port;
+ }
+ catch (NumberFormatException nfe)
+ {
+ nodeHost = node;
+ }
+ }
+ else
+ {
+ try
+ {
+ String potentialPort = node.substring(0, forward);
+ if (potentialPort.length() > 0)
+ {
+ int port = Integer.parseInt(potentialPort);
+ nodePort = "" + port;
+ }
+ }
+ catch (NumberFormatException nfe)
+ {
+ nodeHost = node.substring(0, forward);
+ }
+ nodeBaseUrl = node.substring(forward);
+ }
+ }
+ else
+ {
+ if (forward == -1)
+ {
+ if (colon > 0)
+ {
+ nodeHost = node.substring(0, colon);
+ }
+ if (colon + 1 < node.length())
+ {
+ String port = node.substring(colon + 1);
+ if (port.length() > 0)
+ {
+ nodePort = port;
+ }
+ }
+ }
+ else
+ {
+ if (colon > 0)
+ {
+ nodeHost = node.substring(0, colon);
+ }
+
+ String port = node.substring(colon + 1, forward);
+ if (port.length() > 0)
+ {
+ nodePort = port;
+ }
+ nodeBaseUrl = node.substring(forward);
+
+ }
+ }
+ }
+
+ try
+ {
+ int realPort = Integer.parseInt(nodePort);
+ httpClientsAndBaseURLs.add(new HttpClientAndBaseUrl(httpClientFactory.getHttpClient(nodeHost, realPort), nodeBaseUrl));
+ }
+ catch (NumberFormatException nfe)
+ {
+ httpClientsAndBaseURLs.add(new HttpClientAndBaseUrl(httpClientFactory.getHttpClient(nodeHost, httpClientFactory.getPort()), nodeBaseUrl));
+ }
+ }
+ }
+
+ policy = new ExplicitShardingPolicy(wrapped.getNumShards(), wrapped.getReplicationFactor(), httpClientsAndBaseURLs.size());
+
+ }
+
+
+
+
+ public boolean isSharded()
+ {
+ return wrapped.getNumShards() > 1;
+ }
+
+ public String getShards()
+ {
+
+ if (!policy.configurationIsValid())
+ {
+ throw new AlfrescoRuntimeException("Invalid shard configuration: shard = "
+ + wrapped.getNumShards() + " reoplicationFactor = " + wrapped.getReplicationFactor() + " with node count = " + httpClientsAndBaseURLs.size());
+ }
+
+ return getShards2();
+ }
+
+ private String getShards1()
+ {
+ try
+ {
+ URLCodec encoder = new URLCodec();
+ StringBuilder builder = new StringBuilder();
+
+ Set shards = new HashSet();
+ for (int i = 0; i < httpClientsAndBaseURLs.size(); i += wrapped.getReplicationFactor())
+ {
+ for (Integer shardId : policy.getShardIdsForNode(i + 1))
+ {
+ if (!shards.contains(shardId % wrapped.getNumShards()))
+ {
+ if (shards.size() > 0)
+ {
+ builder.append(',');
+ }
+ HttpClientAndBaseUrl httpClientAndBaseUrl = httpClientsAndBaseURLs.toArray(new HttpClientAndBaseUrl[0])[i];
+ builder.append(encoder.encode(httpClientAndBaseUrl.getHost(), "UTF-8"));
+ builder.append(':');
+ builder.append(encoder.encode("" + httpClientAndBaseUrl.getPort(), "UTF-8"));
+ if (httpClientAndBaseUrl.getBaseUrl().startsWith("/"))
+ {
+ builder.append(encoder.encode(httpClientAndBaseUrl.getBaseUrl(), "UTF-8"));
+ }
+ else
+ {
+ builder.append(encoder.encode("/" + httpClientAndBaseUrl.getBaseUrl(), "UTF-8"));
+ }
+
+ builder.append('-').append(shardId);
+
+ shards.add(shardId % wrapped.getNumShards());
+ }
+
+ }
+ }
+ return builder.toString();
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new LuceneQueryParserException("", e);
+ }
+ }
+
+ private String getShards2()
+ {
+ try
+ {
+ URLCodec encoder = new URLCodec();
+ StringBuilder builder = new StringBuilder();
+
+ for (int shard = 0; shard < wrapped.getNumShards(); shard++)
+ {
+ int position = random.nextInt(wrapped.getReplicationFactor());
+ List nodeInstances = policy.getNodeInstancesForShardId(shard);
+ Integer nodeId = nodeInstances.get(position);
+
+ if (builder.length() > 0)
+ {
+ builder.append(',');
+ }
+ HttpClientAndBaseUrl httpClientAndBaseUrl = httpClientsAndBaseURLs.toArray(new HttpClientAndBaseUrl[0])[nodeId-1];
+ builder.append(encoder.encode(httpClientAndBaseUrl.getHost(), "UTF-8"));
+ builder.append(':');
+ builder.append(encoder.encode("" + httpClientAndBaseUrl.getPort(), "UTF-8"));
+ if (httpClientAndBaseUrl.getBaseUrl().startsWith("/"))
+ {
+ builder.append(encoder.encode(httpClientAndBaseUrl.getBaseUrl(), "UTF-8"));
+ }
+ else
+ {
+ builder.append(encoder.encode("/" + httpClientAndBaseUrl.getBaseUrl(), "UTF-8"));
+ }
+
+ builder.append('-').append(shard);
+
+ }
+ return builder.toString();
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new LuceneQueryParserException("", e);
+ }
+ }
+
+ /**
+ * @return
+ */
+ public int getNodeCount()
+ {
+ return httpClientsAndBaseURLs.size();
+ }
+
+ private static class HttpClientAndBaseUrl
+ {
+
+ private HttpClient httpClient;
+
+ private String baseUrl;
+
+ HttpClientAndBaseUrl(HttpClient httpClient, String baseUrl)
+ {
+ this.httpClient = httpClient;
+ this.baseUrl = baseUrl;
+ }
+
+ public String getBaseUrl()
+ {
+ return baseUrl;
+ }
+
+ public String getHost()
+ {
+ return httpClient.getHostConfiguration().getHost();
+ }
+
+ public int getPort()
+ {
+ return httpClient.getHostConfiguration().getPort();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((baseUrl == null) ? 0 : baseUrl.hashCode());
+ result = prime * result + ((getHost() == null) ? 0 : getHost().hashCode());
+ result = prime * result + getPort();
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ HttpClientAndBaseUrl other = (HttpClientAndBaseUrl) obj;
+ if (baseUrl == null)
+ {
+ if (other.baseUrl != null)
+ return false;
+ }
+ else if (!baseUrl.equals(other.baseUrl))
+ return false;
+ if (httpClient == null)
+ {
+ if (other.httpClient != null)
+ return false;
+ }
+ else if (!getHost().equals(other.getHost()))
+ return false;
+ else if (getPort() != other.getPort())
+ return false;
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "HttpClientAndBaseUrl [getBaseUrl()=" + getBaseUrl() + ", getHost()=" + getHost() + ", getPort()=" + getPort() + "]";
+ }
+
+ }
+
+ /**
+ * @return
+ */
+ public Pair getHttpClientAndBaseUrl()
+ {
+
+ if (!policy.configurationIsValid())
+ {
+ throw new AlfrescoRuntimeException("Invalid shard configuration: shard = "
+ + wrapped.getNumShards() + " reoplicationFactor = " + wrapped.getReplicationFactor() + " with node count = " + httpClientsAndBaseURLs.size());
+ }
+
+ int shard = random.nextInt(wrapped.getNumShards());
+ int position = random.nextInt(wrapped.getReplicationFactor());
+ List nodeInstances = policy.getNodeInstancesForShardId(shard);
+ Integer nodeId = nodeInstances.get(position);
+ HttpClientAndBaseUrl httpClientAndBaseUrl = httpClientsAndBaseURLs.toArray(new HttpClientAndBaseUrl[0])[nodeId-1];
+ return new Pair<>(httpClientAndBaseUrl.httpClient, isSharded() ? httpClientAndBaseUrl.baseUrl+"-"+shard : httpClientAndBaseUrl.baseUrl);
+ }
+
+}
diff --git a/source/test-java/org/alfresco/AllUnitTestsSuite.java b/source/test-java/org/alfresco/AllUnitTestsSuite.java
index 291a346aa5..9da4397a3b 100644
--- a/source/test-java/org/alfresco/AllUnitTestsSuite.java
+++ b/source/test-java/org/alfresco/AllUnitTestsSuite.java
@@ -100,5 +100,6 @@ public class AllUnitTestsSuite extends TestSuite
suite.addTest(new JUnit4TestAdapter(org.alfresco.repo.search.impl.solr.facet.SolrFacetComparatorTest.class));
suite.addTest(new JUnit4TestAdapter(org.alfresco.util.BeanExtenderUnitTest.class));
suite.addTest(new JUnit4TestAdapter(org.alfresco.repo.search.impl.solr.SpellCheckDecisionManagerTest.class));
+ suite.addTest(new JUnit4TestAdapter(org.alfresco.repo.search.impl.solr.SolrStoreMappingWrapperTest.class));
}
}
diff --git a/source/test-java/org/alfresco/repo/search/impl/solr/SolrStoreMappingWrapperTest.java b/source/test-java/org/alfresco/repo/search/impl/solr/SolrStoreMappingWrapperTest.java
new file mode 100644
index 0000000000..ed600c7a15
--- /dev/null
+++ b/source/test-java/org/alfresco/repo/search/impl/solr/SolrStoreMappingWrapperTest.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2005-2015 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.repo.search.impl.solr;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+
+import org.alfresco.httpclient.HttpClientFactory;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.util.Pair;
+import org.apache.commons.httpclient.HostConfiguration;
+import org.apache.commons.httpclient.HttpClient;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.springframework.beans.factory.BeanFactory;
+
+/**
+ * @author Andy
+ *
+ */
+
+@RunWith(MockitoJUnitRunner.class)
+public class SolrStoreMappingWrapperTest
+{
+ SolrStoreMapping mapping;
+
+ SolrStoreMappingWrapper wrapper;
+
+ @Mock
+ HttpClientFactory httpClientFactory;
+
+ @Mock
+ HttpClient httpClientCommon;
+
+ @Mock
+ HttpClient httpClient1;
+
+ @Mock
+ HttpClient httpClient2;
+
+ @Mock
+ HttpClient httpClient3;
+
+ @Mock
+ HttpClient httpClient4;
+
+ @Mock
+ HttpClient httpClient5;
+
+ @Mock
+ HttpClient httpClient6;
+
+ @Mock
+ HttpClient httpClient7;
+
+ @Mock
+ HttpClient httpClient8;
+
+ @Mock
+ HttpClient httpClient9;
+
+ @Mock
+ HostConfiguration hostConfigurationCommon;
+
+ @Mock
+ HostConfiguration hostConfiguration1;
+
+ @Mock
+ HostConfiguration hostConfiguration2;
+
+ @Mock
+ HostConfiguration hostConfiguration3;
+
+ @Mock
+ HostConfiguration hostConfiguration4;
+
+ @Mock
+ HostConfiguration hostConfiguration5;
+
+ @Mock
+ HostConfiguration hostConfiguration6;
+
+ @Mock
+ HostConfiguration hostConfiguration7;
+
+ @Mock
+ HostConfiguration hostConfiguration8;
+
+ @Mock
+ HostConfiguration hostConfiguration9;
+
+ private SolrStoreMapping unsharded;
+
+ private SolrStoreMappingWrapper unshardedWrapper;
+
+ @Mock
+ private BeanFactory beanFactory;
+
+
+
+
+ @Before
+ public void init()
+ {
+ doReturn("common").when(hostConfigurationCommon).getHost();
+ doReturn(999).when(hostConfigurationCommon).getPort();
+
+ doReturn("host").when(hostConfiguration1).getHost();
+ doReturn(999).when(hostConfiguration1).getPort();
+
+ doReturn("common").when(hostConfiguration2).getHost();
+ doReturn(123).when(hostConfiguration2).getPort();
+
+ doReturn("port").when(hostConfiguration3).getHost();
+ doReturn(234).when(hostConfiguration3).getPort();
+
+ doReturn("full").when(hostConfiguration4).getHost();
+ doReturn(345).when(hostConfiguration4).getPort();
+
+ doReturn("common").when(hostConfiguration5).getHost();
+ doReturn(456).when(hostConfiguration5).getPort();
+
+ doReturn("base").when(hostConfiguration6).getHost();
+ doReturn(999).when(hostConfiguration6).getPort();
+
+ doReturn("common").when(hostConfiguration7).getHost();
+ doReturn(567).when(hostConfiguration7).getPort();
+
+ doReturn("common").when(hostConfiguration8).getHost();
+ doReturn(678).when(hostConfiguration8).getPort();
+
+ doReturn("common").when(hostConfiguration9).getHost();
+ doReturn(789).when(hostConfiguration9).getPort();
+
+ doReturn(hostConfigurationCommon).when(httpClientCommon).getHostConfiguration();
+ doReturn(hostConfiguration1).when(httpClient1).getHostConfiguration();
+ doReturn(hostConfiguration2).when(httpClient2).getHostConfiguration();
+ doReturn(hostConfiguration3).when(httpClient3).getHostConfiguration();
+ doReturn(hostConfiguration4).when(httpClient4).getHostConfiguration();
+ doReturn(hostConfiguration5).when(httpClient5).getHostConfiguration();
+ doReturn(hostConfiguration6).when(httpClient6).getHostConfiguration();
+ doReturn(hostConfiguration7).when(httpClient7).getHostConfiguration();
+ doReturn(hostConfiguration8).when(httpClient8).getHostConfiguration();
+ doReturn(hostConfiguration9).when(httpClient9).getHostConfiguration();
+
+ doReturn(httpClientCommon).when(httpClientFactory).getHttpClient();
+ doReturn("common").when(httpClientFactory).getHost();
+ doReturn(999).when(httpClientFactory).getPort();
+ doReturn(httpClientCommon).when(httpClientFactory).getHttpClient(eq("common"), eq(999));
+ doReturn(httpClient1).when(httpClientFactory).getHttpClient(eq("host"), eq(999));
+ doReturn(httpClient2).when(httpClientFactory).getHttpClient(eq("common"), eq(123));
+ doReturn(httpClient3).when(httpClientFactory).getHttpClient(eq("port"), eq(234));
+ doReturn(httpClient4).when(httpClientFactory).getHttpClient(eq("full"), eq(345));
+ doReturn(httpClient5).when(httpClientFactory).getHttpClient(eq("common"), eq(456));
+ doReturn(httpClient6).when(httpClientFactory).getHttpClient(eq("base"), eq(999));
+ doReturn(httpClient7).when(httpClientFactory).getHttpClient(eq("common"), eq(567));
+ doReturn(httpClient8).when(httpClientFactory).getHttpClient(eq("common"), eq(678));
+ doReturn(httpClient9).when(httpClientFactory).getHttpClient(eq("common"), eq(789));
+
+ doReturn(httpClientFactory).when(beanFactory).getBean(eq("httpClientFactory"));
+
+
+ mapping = new SolrStoreMapping();
+ mapping.setBaseUrl("/solr4");
+ mapping.setHttpClientFactory("httpClientFactory");
+ mapping.setIdentifier(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getIdentifier());
+ mapping.setNodeString("host, 123, /woof, port:234, full:345/meep/sheep, 456/cabbage, base/url,,:,:/,:567,:678/,789/more,/");
+ mapping.setNumShards(24);
+ mapping.setProtocol(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getProtocol());
+ mapping.setReplicationFactor(3);
+ wrapper = new SolrStoreMappingWrapper(mapping, beanFactory);
+
+
+
+ unsharded = new SolrStoreMapping();
+ unsharded.setBaseUrl("/solr4");
+ unsharded.setHttpClientFactory("httpClientFactory");
+ unsharded.setIdentifier(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getIdentifier());
+ unsharded.setProtocol(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getProtocol());
+ unshardedWrapper = new SolrStoreMappingWrapper(unsharded, beanFactory);
+ }
+
+
+ @Test
+ public void testBasics()
+ {
+ assertEquals(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getIdentifier(), mapping.getIdentifier());
+ assertEquals(24, mapping.getNumShards());
+ assertEquals(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getProtocol() , mapping.getProtocol());
+ assertEquals(3, mapping.getReplicationFactor());
+ assertEquals(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, mapping.getStoreRef());
+ assertEquals(12, wrapper.getNodeCount());
+ assertTrue(wrapper.isSharded());
+ }
+
+
+ @Test
+ public void testShards()
+ {
+ String shards = wrapper.getShards();
+ assertNotNull(shards);
+ assertTrue(shards.length() > 0);
+ String[] fragments = shards.split(",");
+ assertEquals(mapping.getNumShards(), fragments.length);
+ }
+
+ @Test
+ public void testDistribution()
+ {
+ // default seed gives /woof
+ Pair distributor = wrapper.getHttpClientAndBaseUrl();
+ assertNotNull(distributor);
+ assertEquals("/woof-14", distributor.getSecond());
+ assertEquals("common", distributor.getFirst().getHostConfiguration().getHost());
+ assertEquals(999, distributor.getFirst().getHostConfiguration().getPort());
+ }
+
+ @Test
+ public void testUnsharded()
+ {
+ assertTrue(unshardedWrapper.isSharded() == false);
+
+ Pair distributor = unshardedWrapper.getHttpClientAndBaseUrl();
+ assertNotNull(distributor);
+ assertEquals("/solr4", distributor.getSecond());
+ assertEquals("common", distributor.getFirst().getHostConfiguration().getHost());
+ assertEquals(999, distributor.getFirst().getHostConfiguration().getPort());
+ }
+
+}