diff --git a/search-services/alfresco-solrclient-lib/source/java/org/alfresco/solr/client/SOLRAPIClientFactory.java b/search-services/alfresco-solrclient-lib/source/java/org/alfresco/solr/client/SOLRAPIClientFactory.java index 554089d8b..b2a8efac2 100644 --- a/search-services/alfresco-solrclient-lib/source/java/org/alfresco/solr/client/SOLRAPIClientFactory.java +++ b/search-services/alfresco-solrclient-lib/source/java/org/alfresco/solr/client/SOLRAPIClientFactory.java @@ -1,6 +1,6 @@ /* * #%L - * Alfresco Solr Client + * Alfresco Data model classes * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% @@ -23,60 +23,60 @@ * along with Alfresco. If not, see . * #L% */ - -package org.alfresco.solr.client; - -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -import org.alfresco.encryption.KeyResourceLoader; -import org.alfresco.encryption.KeyStoreParameters; -import org.alfresco.encryption.ssl.SSLEncryptionParameters; -import org.alfresco.httpclient.AlfrescoHttpClient; -import org.alfresco.httpclient.HttpClientFactory; -import org.alfresco.httpclient.HttpClientFactory.SecureCommsType; -import org.alfresco.repo.dictionary.NamespaceDAO; -import org.alfresco.service.cmr.dictionary.DictionaryService; - -/** - * This factory encapsulates the creation of a SOLRAPIClient and the management of that resource. - * - * @author Ahmed Owian - */ -public class SOLRAPIClientFactory -{ - /* - * Pool of cached client resources keyed on alfresco instances - */ - private static Map clientsPerAlfresco = new HashMap<>(); - - // encryption related parameters - private String secureCommsType; // "none", "https" - private String keyStoreType; - private String keyStoreProvider; - private String passwordFileLocation; - private String keyStoreLocation; - - // ssl - private String sslKeyStoreType; - private String sslKeyStoreProvider; - private String sslKeyStoreLocation; - private String sslKeyStorePasswordFileLocation; - private String sslTrustStoreType; - private String sslTrustStoreProvider; - private String sslTrustStoreLocation; - private String sslTrustStorePasswordFileLocation; - private String alfrescoHost; - private int alfrescoPort; - private int alfrescoPortSSL; - private String baseUrl; - - // http client - private int maxTotalConnections = 40; - private int maxHostConnections = 40; - private int socketTimeout = 120000; - + +package org.alfresco.solr.client; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.alfresco.encryption.KeyResourceLoader; +import org.alfresco.encryption.KeyStoreParameters; +import org.alfresco.encryption.ssl.SSLEncryptionParameters; +import org.alfresco.httpclient.AlfrescoHttpClient; +import org.alfresco.httpclient.HttpClientFactory; +import org.alfresco.httpclient.HttpClientFactory.SecureCommsType; +import org.alfresco.repo.dictionary.NamespaceDAO; +import org.alfresco.service.cmr.dictionary.DictionaryService; + +/** + * This factory encapsulates the creation of a SOLRAPIClient and the management of that resource. + * + * @author Ahmed Owian + */ +public class SOLRAPIClientFactory +{ + /* + * Pool of cached client resources keyed on alfresco instances + */ + private static Map clientsPerAlfresco = new HashMap<>(); + + // encryption related parameters + private String secureCommsType; // "none", "https" + private String keyStoreType; + private String keyStoreProvider; + private String passwordFileLocation; + private String keyStoreLocation; + + // ssl + private String sslKeyStoreType; + private String sslKeyStoreProvider; + private String sslKeyStoreLocation; + private String sslKeyStorePasswordFileLocation; + private String sslTrustStoreType; + private String sslTrustStoreProvider; + private String sslTrustStoreLocation; + private String sslTrustStorePasswordFileLocation; + private String alfrescoHost; + private int alfrescoPort; + private int alfrescoPortSSL; + private String baseUrl; + + // http client + private int maxTotalConnections = 40; + private int maxHostConnections = 40; + private int socketTimeout = 120000; + public static void close() { for(SOLRAPIClient client : clientsPerAlfresco.values()) { @@ -84,112 +84,118 @@ public class SOLRAPIClientFactory } } - /** - * Gets the client resource from the pool. - * - * @param alfrescoHost String - * @param alfrescoPort int - * @param alfrescoPortSSL int - * @return SOLRAPIClient - */ - private SOLRAPIClient getCachedClient(String alfrescoHost, int alfrescoPort, int alfrescoPortSSL) - { - String key = constructKey(alfrescoHost, alfrescoPort, alfrescoPortSSL); - return clientsPerAlfresco.get(key); - } - - /** - * Constructs a key to identify a unique alfresco instance to which the client will connect. - * - * @param alfrescoHost String - * @param alfrescoPort int - * @param alfrescoPortSSL int - * @return the key to get a client - */ - private String constructKey(String alfrescoHost, int alfrescoPort, int alfrescoPortSSL) - { - return alfrescoHost + alfrescoPort + alfrescoPortSSL; - } - - /** - * Sets the client in the resource pool. - * - * @param alfrescoHost String - * @param alfrescoPort int - * @param alfrescoPortSSL int - * @param client SOLRAPIClient - */ - private void setCachedClient(String alfrescoHost, int alfrescoPort, int alfrescoPortSSL, SOLRAPIClient client) - { - String key = constructKey(alfrescoHost, alfrescoPort, alfrescoPortSSL); - clientsPerAlfresco.put(key, client); - } - - /** - * Creates the SOLRAPIClient or gets it from a pool - * - * @param props solrcore.properties in the /conf directory - * @param keyResourceLoader reads encryption key resources - * @param dictionaryService represents the Repository Data Dictionary - * @param namespaceDAO allows retrieving and creating Namespace definitions - * @return an instance of SOLRAPIClient - */ - public SOLRAPIClient getSOLRAPIClient(Properties props, KeyResourceLoader keyResourceLoader, - DictionaryService dictionaryService, NamespaceDAO namespaceDAO) - { - alfrescoHost = props.getProperty("alfresco.host", "localhost"); - alfrescoPort = Integer.parseInt(props.getProperty("alfresco.port", "8080")); - alfrescoPortSSL = Integer.parseInt(props.getProperty("alfresco.port.ssl", "8443")); - - SOLRAPIClient client = getCachedClient(alfrescoHost, alfrescoPort, alfrescoPortSSL); - if (client == null) - { - baseUrl = props.getProperty("alfresco.baseUrl", "/alfresco"); - keyStoreType = props.getProperty("alfresco.encryption.keystore.type", "JCEKS"); - keyStoreProvider = props.getProperty("alfresco.encryption.keystore.provider"); - passwordFileLocation = props.getProperty("alfresco.encryption.keystore.passwordFileLocation"); - keyStoreLocation = props.getProperty("alfresco.encryption.keystore.location"); - sslKeyStoreType = props.getProperty("alfresco.encryption.ssl.keystore.type", "JCEKS"); - sslKeyStoreProvider = props.getProperty("alfresco.encryption.ssl.keystore.provider", ""); - sslKeyStoreLocation = props.getProperty("alfresco.encryption.ssl.keystore.location", - "ssl.repo.client.keystore"); - sslKeyStorePasswordFileLocation = props.getProperty( - "alfresco.encryption.ssl.keystore.passwordFileLocation", "ssl-keystore-passwords.properties"); - sslTrustStoreType = props.getProperty("alfresco.encryption.ssl.truststore.type", "JCEKS"); - sslTrustStoreProvider = props.getProperty("alfresco.encryption.ssl.truststore.provider", ""); - sslTrustStoreLocation = props.getProperty("alfresco.encryption.ssl.truststore.location", - "ssl.repo.client.truststore"); - sslTrustStorePasswordFileLocation = props.getProperty( - "alfresco.encryption.ssl.truststore.passwordFileLocation", - "ssl-truststore-passwords.properties"); - secureCommsType = props.getProperty("alfresco.secureComms", "none"); - maxTotalConnections = Integer.parseInt(props.getProperty("alfresco.maxTotalConnections", "40")); - maxHostConnections = Integer.parseInt(props.getProperty("alfresco.maxHostConnections", "40")); - socketTimeout = Integer.parseInt(props.getProperty("alfresco.socketTimeout", "60000")); - - client = new SOLRAPIClient(getRepoClient(keyResourceLoader), dictionaryService, namespaceDAO); - setCachedClient(alfrescoHost, alfrescoPort, alfrescoPortSSL, client); - } - - return client; - } - - protected AlfrescoHttpClient getRepoClient(KeyResourceLoader keyResourceLoader) - { - // TODO i18n - KeyStoreParameters keyStoreParameters = new KeyStoreParameters("SSL Key Store", sslKeyStoreType, - sslKeyStoreProvider, sslKeyStorePasswordFileLocation, sslKeyStoreLocation); - KeyStoreParameters trustStoreParameters = new KeyStoreParameters("SSL Trust Store", sslTrustStoreType, - sslTrustStoreProvider, sslTrustStorePasswordFileLocation, sslTrustStoreLocation); - SSLEncryptionParameters sslEncryptionParameters = new SSLEncryptionParameters(keyStoreParameters, - trustStoreParameters); - - HttpClientFactory httpClientFactory = new HttpClientFactory(SecureCommsType.getType(secureCommsType), - sslEncryptionParameters, keyResourceLoader, null, null, alfrescoHost, alfrescoPort, - alfrescoPortSSL, maxTotalConnections, maxHostConnections, socketTimeout); - // TODO need to make port configurable depending on secure comms, or just make redirects work - AlfrescoHttpClient repoClient = httpClientFactory.getRepoClient(alfrescoHost, alfrescoPortSSL); - repoClient.setBaseUrl(baseUrl); - return repoClient; - } -} + /** + * Gets the client resource from the pool. + * + * @param alfrescoHost String + * @param alfrescoPort int + * @param alfrescoPortSSL int + * @return SOLRAPIClient + */ + private SOLRAPIClient getCachedClient(String alfrescoHost, int alfrescoPort, int alfrescoPortSSL) + { + String key = constructKey(alfrescoHost, alfrescoPort, alfrescoPortSSL); + return clientsPerAlfresco.get(key); + } + + /** + * Constructs a key to identify a unique alfresco instance to which the client will connect. + * + * @param alfrescoHost String + * @param alfrescoPort int + * @param alfrescoPortSSL int + * @return the key to get a client + */ + private String constructKey(String alfrescoHost, int alfrescoPort, int alfrescoPortSSL) + { + return alfrescoHost + alfrescoPort + alfrescoPortSSL; + } + + /** + * Sets the client in the resource pool. + * + * @param alfrescoHost String + * @param alfrescoPort int + * @param alfrescoPortSSL int + * @param client SOLRAPIClient + */ + private void setCachedClient(String alfrescoHost, int alfrescoPort, int alfrescoPortSSL, SOLRAPIClient client) + { + String key = constructKey(alfrescoHost, alfrescoPort, alfrescoPortSSL); + clientsPerAlfresco.put(key, client); + } + + /** + * Creates the SOLRAPIClient or gets it from a pool + * + * @param props solrcore.properties in the /conf directory + * @param keyResourceLoader reads encryption key resources + * @param dictionaryService represents the Repository Data Dictionary + * @param namespaceDAO allows retrieving and creating Namespace definitions + * @return an instance of SOLRAPIClient + */ + public SOLRAPIClient getSOLRAPIClient(Properties props, KeyResourceLoader keyResourceLoader, + DictionaryService dictionaryService, NamespaceDAO namespaceDAO) + { + + if(Boolean.parseBoolean(System.getProperty("alfresco.test", "false"))) + { + return new SOLRAPIQueueClient(namespaceDAO); + } + + alfrescoHost = props.getProperty("alfresco.host", "localhost"); + alfrescoPort = Integer.parseInt(props.getProperty("alfresco.port", "8080")); + alfrescoPortSSL = Integer.parseInt(props.getProperty("alfresco.port.ssl", "8443")); + + SOLRAPIClient client = getCachedClient(alfrescoHost, alfrescoPort, alfrescoPortSSL); + if (client == null) + { + baseUrl = props.getProperty("alfresco.baseUrl", "/alfresco"); + keyStoreType = props.getProperty("alfresco.encryption.keystore.type", "JCEKS"); + keyStoreProvider = props.getProperty("alfresco.encryption.keystore.provider"); + passwordFileLocation = props.getProperty("alfresco.encryption.keystore.passwordFileLocation"); + keyStoreLocation = props.getProperty("alfresco.encryption.keystore.location"); + sslKeyStoreType = props.getProperty("alfresco.encryption.ssl.keystore.type", "JCEKS"); + sslKeyStoreProvider = props.getProperty("alfresco.encryption.ssl.keystore.provider", ""); + sslKeyStoreLocation = props.getProperty("alfresco.encryption.ssl.keystore.location", + "ssl.repo.client.keystore"); + sslKeyStorePasswordFileLocation = props.getProperty( + "alfresco.encryption.ssl.keystore.passwordFileLocation", "ssl-keystore-passwords.properties"); + sslTrustStoreType = props.getProperty("alfresco.encryption.ssl.truststore.type", "JCEKS"); + sslTrustStoreProvider = props.getProperty("alfresco.encryption.ssl.truststore.provider", ""); + sslTrustStoreLocation = props.getProperty("alfresco.encryption.ssl.truststore.location", + "ssl.repo.client.truststore"); + sslTrustStorePasswordFileLocation = props.getProperty( + "alfresco.encryption.ssl.truststore.passwordFileLocation", + "ssl-truststore-passwords.properties"); + secureCommsType = props.getProperty("alfresco.secureComms", "none"); + maxTotalConnections = Integer.parseInt(props.getProperty("alfresco.maxTotalConnections", "40")); + maxHostConnections = Integer.parseInt(props.getProperty("alfresco.maxHostConnections", "40")); + socketTimeout = Integer.parseInt(props.getProperty("alfresco.socketTimeout", "60000")); + + client = new SOLRAPIClient(getRepoClient(keyResourceLoader), dictionaryService, namespaceDAO); + setCachedClient(alfrescoHost, alfrescoPort, alfrescoPortSSL, client); + } + + return client; + } + + protected AlfrescoHttpClient getRepoClient(KeyResourceLoader keyResourceLoader) + { + // TODO i18n + KeyStoreParameters keyStoreParameters = new KeyStoreParameters("SSL Key Store", sslKeyStoreType, + sslKeyStoreProvider, sslKeyStorePasswordFileLocation, sslKeyStoreLocation); + KeyStoreParameters trustStoreParameters = new KeyStoreParameters("SSL Trust Store", sslTrustStoreType, + sslTrustStoreProvider, sslTrustStorePasswordFileLocation, sslTrustStoreLocation); + SSLEncryptionParameters sslEncryptionParameters = new SSLEncryptionParameters(keyStoreParameters, + trustStoreParameters); + + HttpClientFactory httpClientFactory = new HttpClientFactory(SecureCommsType.getType(secureCommsType), + sslEncryptionParameters, keyResourceLoader, null, null, alfrescoHost, alfrescoPort, + alfrescoPortSSL, maxTotalConnections, maxHostConnections, socketTimeout); + // TODO need to make port configurable depending on secure comms, or just make redirects work + AlfrescoHttpClient repoClient = httpClientFactory.getRepoClient(alfrescoHost, alfrescoPortSSL); + repoClient.setBaseUrl(baseUrl); + return repoClient; + } +} diff --git a/search-services/alfresco-solrclient-lib/source/java/org/alfresco/solr/client/SOLRAPIQueueClient.java b/search-services/alfresco-solrclient-lib/source/java/org/alfresco/solr/client/SOLRAPIQueueClient.java new file mode 100644 index 000000000..01a8d4020 --- /dev/null +++ b/search-services/alfresco-solrclient-lib/source/java/org/alfresco/solr/client/SOLRAPIQueueClient.java @@ -0,0 +1,324 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * 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 . + * #L% + */ + +package org.alfresco.solr.client; + +import java.io.*; +import java.util.*; + +import org.alfresco.httpclient.AuthenticationException; +import org.alfresco.httpclient.Response; +import org.alfresco.repo.dictionary.NamespaceDAO; +import org.alfresco.repo.index.shard.ShardState; +import org.alfresco.service.namespace.QName; +import org.apache.commons.codec.EncoderException; +import org.apache.commons.httpclient.HttpStatus; +import org.json.JSONException; + +// TODO error handling, including dealing with a repository that is not responsive (ConnectException in sendRemoteRequest) +// TODO get text content transform status handling +/** + * A client that reads from an internal queue. This is used for test cases. + */ + +public class SOLRAPIQueueClient extends SOLRAPIClient +{ + public static List aclChangeSetQueue = Collections.synchronizedList(new ArrayList()); + public static Map> aclMap = Collections.synchronizedMap(new HashMap()); + public static Map aclReadersMap = Collections.synchronizedMap(new HashMap()); + + public static List transactionQueue = Collections.synchronizedList(new ArrayList()); + public static Map> nodeMap = Collections.synchronizedMap(new HashMap()); + public static Map nodeMetaDataMap = Collections.synchronizedMap(new HashMap()); + + public SOLRAPIQueueClient(NamespaceDAO namespaceDAO) + { + super(null,null,namespaceDAO); + } + + /** + * Get the ACL ChangeSets + * + * @param fromCommitTime the lowest commit time (optional) + * @param minAclChangeSetId the lowest ChangeSet ID (optional) + * @param maxResults the maximum number of results (a reasonable value only) + * @return the ACL ChangeSets in order of commit time and ID + */ + + + public AclChangeSets getAclChangeSets(Long fromCommitTime, Long minAclChangeSetId, Long toCommitTime, Long maxAclChangeSetId, int maxResults) + throws AuthenticationException, IOException, JSONException + { + int size = aclChangeSetQueue.size(); + long maxTime = 0L; + long maxId = 0L; + + if(fromCommitTime == null && toCommitTime == null) + { + List aclChangeSetList = new ArrayList(); + for(int i=0; i= minAclChangeSetId && aclChangeSet.getId() < maxAclChangeSetId) + { + aclChangeSetList.add(aclChangeSet); + maxTime = Math.max(aclChangeSet.getCommitTimeMs(), maxTime); + maxId = Math.max(aclChangeSet.getId(), maxId); + } + + if(aclChangeSetList.size() == maxResults) { + break; + } + } + + return new AclChangeSets(aclChangeSetList, maxTime, maxId); + } + + List aclChangeSetList = new ArrayList(); + + for(int i=0; i toCommitTime) + { + //We have not yet reached this alcChangeSet so break out of the loop + break; + } + else + { + aclChangeSetList.add(aclChangeSet); + maxTime = aclChangeSet.getCommitTimeMs(); + maxId = aclChangeSet.getId(); + + if(aclChangeSetList.size() == maxResults) + { + break; + } + } + } + + return new AclChangeSets(aclChangeSetList, maxTime, maxId); + } + + /** + * Get the ACLs associated with a given list of ACL ChangeSets. The ACLs may be truncated for + * the last ACL ChangeSet in the return values - the ACL count from the + * {@link #getAclChangeSets(Long, Long, Long, Long, int) ACL ChangeSets}. + * + * @param aclChangeSets the ACL ChangeSets to include + * @param minAclId the lowest ACL ID (may be null) + * @param maxResults the maximum number of results to retrieve + * @return the ACLs (includes ChangeSet ID) + */ + public List getAcls(List aclChangeSets, Long minAclId, int maxResults) throws AuthenticationException, IOException, JSONException + { + List allAcls = new ArrayList(); + for(AclChangeSet aclChangeSet : aclChangeSets) + { + List aclList = aclMap.get(aclChangeSet.getId()); + allAcls.addAll(aclList); + } + return allAcls; + } + + /** + * Get the ACL readers for a given list of ACLs + * + * @param acls the ACLs + * @return the readers for the ACLs + */ + public List getAclReaders(List acls) throws AuthenticationException, IOException, JSONException + { + List allAclReaders = new ArrayList(); + for(Acl acl : acls) + { + AclReaders aclReaders = aclReadersMap.get(acl.getId()); + allAclReaders.add(aclReaders); + } + return allAclReaders; + } + + + public List getModelsDiff(List currentModels) throws AuthenticationException, IOException, JSONException + { + return new ArrayList(); + } + + + public Transactions getTransactions(Long fromCommitTime, Long minTxnId, Long toCommitTime, Long maxTxnId, int maxResults) throws AuthenticationException, IOException, JSONException + { + try + { + return getTransactions(fromCommitTime, minTxnId, toCommitTime, maxTxnId, maxResults, null); + } + catch(EncoderException e) + { + throw new IOException(e); + } + } + + + + + public Transactions getTransactions(Long fromCommitTime, Long minTxnId, Long toCommitTime, Long maxTxnId, int maxResults, ShardState shardState) throws AuthenticationException, IOException, JSONException, EncoderException + { + int size = transactionQueue.size(); + + long maxTime = 0L; + long maxId = 0L; + + if(fromCommitTime == null && toCommitTime == null) + { + List transactionList = new ArrayList(); + + for(int i=0; i= minTxnId && txn.getId() < maxTxnId) + { + transactionList.add(txn); + maxTime = Math.max(txn.getCommitTimeMs(), maxTime); + maxId = Math.max(txn.getId(), maxId); + } + + if(transactionList.size() == maxResults) { + break; + } + } + + return new Transactions(transactionList, maxTime, maxId); + } + + List transactionList = new ArrayList(); + + for(int i=0; i toCommitTime) + { + //We have not yet reached this transaction so break out of the loop + break; + } + else + { + //We have a transaction to work with + transactionList.add(txn); + maxTime = txn.getCommitTimeMs(); + maxId = txn.getId(); + + if(transactionList.size() == maxResults) + { + break; + } + } + } + + return new Transactions(transactionList, maxTime, maxId); + } + + public List getNodes(GetNodesParameters parameters, int maxResults) throws AuthenticationException, IOException, JSONException + { + List txnIds = parameters.getTransactionIds(); + List allNodes = new ArrayList(); + for(long txnId : txnIds) + { + List nodes = nodeMap.get(txnId); + allNodes.addAll(nodes); + } + + return allNodes; + } + + public List getNodesMetaData(NodeMetaDataParameters params, int maxResults) throws AuthenticationException, IOException, JSONException + { + List nodeMetaDatas = new ArrayList(); + List nodeIds = params.getNodeIds(); + if(nodeIds != null) { + for (long nodeId : nodeIds) { + NodeMetaData nodeMetaData = nodeMetaDataMap.get(nodeId); + nodeMetaDatas.add(nodeMetaData); + } + } else { + Long fromId = params.getFromNodeId(); + NodeMetaData nodeMetaData = nodeMetaDataMap.get(fromId); + nodeMetaDatas.add(nodeMetaData); + } + + return nodeMetaDatas; + } + + public GetTextContentResponse getTextContent(Long nodeId, QName propertyQName, Long modifiedSince) throws AuthenticationException, IOException + { + //Just put the nodeId innto the content so we query for this in tests. + return new GetTextContentResponse(new DummyResponse("Hello world "+nodeId)); + } + + private class DummyResponse implements Response + { + private String text; + + public DummyResponse(String text) + { + this.text = text; + } + + public InputStream getContentAsStream() + { + return new ByteArrayInputStream(text.getBytes()); + } + + public int getStatus() { + return HttpStatus.SC_OK; + } + + public void release() + { + + } + + public String getHeader(String key) { + return null; + } + + public String getContentType() { + return "text/html"; + } + } + + public void close() + { + + } +}