From 9edd991a5fdb439cff1b91ff0c428aef70c6525b Mon Sep 17 00:00:00 2001 From: Gaurav Mishra Date: Mon, 18 Mar 2024 13:01:14 +0530 Subject: [PATCH 01/10] feat(lucene): nouveau integration Signed-off-by: Gaurav Mishra --- README.md | 6 +- backend/src-common/pom.xml | 6 + .../sw360/common/utils/SearchUtils.java | 66 ++++ .../db/ComponentSearchHandler.java | 134 ++++--- .../db/ModerationSearchHandler.java | 117 +++--- .../db/ObligationElementSearchHandler.java | 60 ++-- .../db/ObligationSearchHandler.java | 70 ++-- .../datahandler/db/PackageSearchHandler.java | 127 ++++--- .../datahandler/db/ProjectSearchHandler.java | 149 ++++---- .../datahandler/db/ReleaseSearchHandler.java | 55 +-- .../datahandler/db/UserSearchHandler.java | 122 ++++--- .../datahandler/db/VendorSearchHandler.java | 57 +-- .../document/SpdxDocumentSearchHandler.java | 48 ++- ...SpdxDocumentCreationInfoSearchHandler.java | 48 ++- .../SpdxPackageInfoSearchHandler.java | 50 ++- .../db/AbstractDatabaseSearchHandler.java | 155 ++++---- .../sw360/users/db/UserDatabaseHandler.java | 6 +- libraries/datahandler/pom.xml | 7 + .../DatabaseConnectorCloudant.java | 4 + .../DatabaseInstanceCloudant.java | 4 + .../sw360/datahandler/common/SW360Utils.java | 4 +- .../lucene/LuceneAwareDatabaseConnector.java | 320 ----------------- .../NouveauLuceneAwareDatabaseConnector.java | 340 ++++++++++++++++++ libraries/nouveau-handler/pom.xml | 137 +++++++ .../org/eclipse/sw360/nouveau/CommonHits.java | 51 +++ .../sw360/nouveau/CommonNouveauResult.java | 54 +++ .../sw360/nouveau/CustomNouveauResult.java | 34 ++ .../nouveau/LuceneAwareCouchDbConnector.java | 101 ++++++ .../eclipse/sw360/nouveau/NouveauQuery.java | 164 +++++++++ .../eclipse/sw360/nouveau/NouveauResult.java | 45 +++ .../designdocument/NouveauDesignDocument.java | 60 ++++ .../NouveauIndexDesignDocument.java | 48 +++ .../designdocument/NouveauIndexFunction.java | 71 ++++ .../eclipse/sw360/nouveau/CommonHitsTest.java | 40 +++ .../LuceneAwareCouchDbConnectorTest.java | 24 ++ libraries/pom.xml | 1 + .../component/ComponentController.java | 5 +- .../packages/SW360PackageService.java | 6 +- .../project/ProjectController.java | 5 +- .../release/ReleaseController.java | 1 - .../resourceserver/user/UserController.java | 8 +- 41 files changed, 1912 insertions(+), 898 deletions(-) create mode 100644 backend/src-common/src/main/java/org/eclipse/sw360/common/utils/SearchUtils.java delete mode 100644 libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/LuceneAwareDatabaseConnector.java create mode 100644 libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java create mode 100644 libraries/nouveau-handler/pom.xml create mode 100644 libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CommonHits.java create mode 100644 libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CommonNouveauResult.java create mode 100644 libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CustomNouveauResult.java create mode 100644 libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java create mode 100644 libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/NouveauQuery.java create mode 100644 libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/NouveauResult.java create mode 100644 libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauDesignDocument.java create mode 100644 libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauIndexDesignDocument.java create mode 100644 libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauIndexFunction.java create mode 100644 libraries/nouveau-handler/src/test/java/org/eclipse/sw360/nouveau/CommonHitsTest.java create mode 100644 libraries/nouveau-handler/src/test/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnectorTest.java diff --git a/README.md b/README.md index eac76af259..7f98cf686c 100644 --- a/README.md +++ b/README.md @@ -35,14 +35,14 @@ This is a multi module maven file. please consider that we have the following mo ### Issues If you run in any issues with documentation or software, please be kind and report to our -[Github issues area](https://github.com/eclipse/sw360/issues). +[GitHub issues area](https://github.com/eclipse/sw360/issues). ### Deployment Is recommended using the docker based setup, [described here](https://github.com/eclipse/sw360/blob/main/README_DOCKER.md). -If you intend to install in a bare metal machine or use in your own virtualizaed system, [bare metal instructions are provided here](https://www.eclipse.org/sw360/docs/deployment/baremetal/deploy-natively/). +If you intend to install in a bare metal machine or use in your own virtualized system, [bare metal instructions are provided here](https://www.eclipse.org/sw360/docs/deployment/baremetal/deploy-natively/). ### Development @@ -89,7 +89,7 @@ mvn package -P deploy \ -Dbackend.deploy.dir=webapps ``` -If you want run the the tests, we need start a local couchdb server and Docker is required: +If you want to run the tests, we need start a local couchdb server and Docker is required: ### License diff --git a/backend/src-common/pom.xml b/backend/src-common/pom.xml index 5462fd0509..f439d5ee9e 100644 --- a/backend/src-common/pom.xml +++ b/backend/src-common/pom.xml @@ -63,5 +63,11 @@ com.github.package-url packageurl-java + + org.eclipse.sw360 + nouveau-handler + ${project.version} + compile + diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/common/utils/SearchUtils.java b/backend/src-common/src/main/java/org/eclipse/sw360/common/utils/SearchUtils.java new file mode 100644 index 0000000000..619af56566 --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/common/utils/SearchUtils.java @@ -0,0 +1,66 @@ +/* + * Copyright Siemens AG, 2024. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.eclipse.sw360.common.utils; + +public class SearchUtils { + /* + * This function returns the entire document as a string which can then be + * indexed as a text field for 'default' index in Nouveau. + * Possible values for `typeof` are documented at + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof#description + * We entertain only following types: + * - number, bigint, string -> Directly converted to string with `+` + * - boolean -> Converted to string using `toString()` + * - object -> Recursively converted to string. + * - function & others -> skip + */ + public static final String OBJ_TO_DEFAULT_INDEX = " function getObjAsString(obj) {" + + " let result = '';" + + " for (var key in obj) {" + + " if (key == '_rev' || key == 'type') continue;" + + " switch (typeof(obj[key])) {" + + " case 'object':" + + " if (obj[key] !== null) {" + + " result += ' ' + getObjAsString(obj[key]);" + + " }" + + " break;" + + " case 'number':" + + " case 'bigint':" + + " case 'string':" + + " result += ' ' + obj[key];" + + " break;" + + " case 'boolean':" + + " result += ' ' + obj[key].toString();" + + " break;" + + " case 'function':" + + " default:" + + " break;" + + " }" + + " }" + + " return result.trim();" + + " };"; + + /* + * This function takes an array (or object) and traverse through it. Get all + * the values and index them as a text index. + */ + public static final String OBJ_ARRAY_TO_STRING_INDEX = " function arrayToStringIndex(arr, indexName) {" + + " let result = '';" + + " for (let i in arr) {" + + " if (arr[i] && typeof(arr[i]) == 'string' && arr[i].length > 0) {" + + " result += ' ' + arr[i];" + + " }" + + " }" + + " if (result.trim().length > 0) {" + + " index('text', indexName, result.trim(), {'store': true});" + + " }" + + " }"; +} diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java index 4115e97f21..e248bd30f2 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java @@ -9,18 +9,21 @@ */ package org.eclipse.sw360.datahandler.db; -import org.eclipse.sw360.datahandler.common.DatabaseSettings; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; +import com.cloudant.client.api.CloudantClient; +import com.google.gson.Gson; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.components.Component; import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; import org.eclipse.sw360.datahandler.thrift.users.User; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; +import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; import org.ektorp.http.HttpClient; -import com.cloudant.client.api.CloudantClient; - import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -28,7 +31,9 @@ import java.util.Set; import java.util.function.Supplier; +import static org.eclipse.sw360.common.utils.SearchUtils.OBJ_ARRAY_TO_STRING_INDEX; import static org.eclipse.sw360.datahandler.permissions.PermissionUtils.makePermission; +import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; /** * Class for accessing the Lucene connector on the CouchDB database @@ -40,75 +45,61 @@ public class ComponentSearchHandler { private static final Logger log = LogManager.getLogger(ComponentSearchHandler.class); - private static final LuceneSearchView luceneSearchView = new LuceneSearchView("lucene", "components", - "function(doc) {" + - " var ret = new Document();" + - " if(!doc.type) return ret;" + - " if(doc.type != 'component') return ret;" + - " function idx(obj) {" + - " for (var key in obj) {" + - " switch (typeof obj[key]) {" + - " case 'object':" + - " idx(obj[key]);" + - " break;" + - " case 'function':" + - " break;" + - " default:" + - " ret.add(obj[key]);" + - " break;" + - " }" + - " }" + - " };" + - " idx(doc);" + - " for(var i in doc.categories) {" + - " ret.add(doc.categories[i], {\"field\": \"categories\"} );" + - " }" + - " for(var i in doc.languages) {" + - " ret.add(doc.languages[i], {\"field\": \"languages\"} );" + - " }" + - " for(var i in doc.softwarePlatforms) {" + - " ret.add(doc.softwarePlatforms[i], {\"field\": \"softwarePlatforms\"} );" + - " }" + - " for(var i in doc.operatingSystems) {" + - " ret.add(doc.operatingSystems[i], {\"field\": \"operatingSystems\"} );" + - " }" + - " for(var i in doc.vendorNames) {" + - " ret.add(doc.vendorNames[i], {\"field\": \"vendorNames\"} );" + - " }" + - " for(var i in doc.mainLicenseIds) {" + - " ret.add(doc.mainLicenseIds[i], {\"field\": \"mainLicenseIds\"} );" + - " }" + - " ret.add(doc.componentType, {\"field\": \"componentType\"} );" + - " if(doc.name !== undefined && doc.name != null && doc.name.length >0) { "+ - " ret.add(doc.name, {\"field\": \"name\"} );" + - " }" + - " if(doc.createdBy && doc.createdBy.length) { "+ - " ret.add(doc.createdBy, {\"field\": \"createdBy\"} );" + - " }" + - " if(doc.createdOn && doc.createdOn.length) { "+ - " ret.add(doc.createdOn, {\"field\": \"createdOn\", \"type\": \"date\"} );" + - " }" + - " if(doc.businessUnit && doc.businessUnit.length) { "+ - " ret.add(doc.businessUnit, {\"field\": \"businessUnit\"} );" + - " }" + - " return ret;" + - "}"); + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + private static final NouveauIndexDesignDocument luceneSearchView + = new NouveauIndexDesignDocument("components", + new NouveauIndexFunction( + "function(doc) {" + + OBJ_ARRAY_TO_STRING_INDEX + + " if(!doc.type || doc.type != 'component') return;" + + " arrayToStringIndex(doc.categories, 'categories');" + + " arrayToStringIndex(doc.languages, 'languages');" + + " arrayToStringIndex(doc.softwarePlatforms, 'softwarePlatforms');" + + " arrayToStringIndex(doc.operatingSystems, 'operatingSystems');" + + " arrayToStringIndex(doc.vendorNames, 'vendorNames');" + + " arrayToStringIndex(doc.mainLicenseIds, 'mainLicenseIds');" + + " if(doc.componentType && typeof(doc.componentType) == 'string' && doc.componentType.length > 0) {" + + " index('text', 'componentType', doc.componentType, {'store': true});" + + " }" + + " if(doc.name && typeof(doc.name) == 'string' && doc.name.length > 0) {" + + " index('text', 'name', doc.name, {'store': true});"+ + " }" + + " if(doc.createdBy && typeof(doc.createdBy) == 'string' && doc.createdBy.length > 0) {" + + " index('text', 'createdBy', doc.createdBy, {'store': true});"+ + " }" + + " if(doc.createdOn && doc.createdOn.length) {"+ + " var dt = new Date(doc.createdOn);"+ + " var formattedDt = `${dt.getFullYear()}${(dt.getMonth()+1).toString().padStart(2,'0')}${dt.getDate().toString().padStart(2,'0')}`;" + + " index('double', 'createdOn', Number(formattedDt), {'store': true});"+ + " }" + + " if(doc.businessUnit && typeof(doc.businessUnit) == 'string' && doc.businessUnit.length > 0) {" + + " index('text', 'businessUnit', doc.businessUnit, {'store': true});"+ + " }" + + "}")); - private final LuceneAwareDatabaseConnector connector; + + private final NouveauLuceneAwareDatabaseConnector connector; public ComponentSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { - connector = new LuceneAwareDatabaseConnector(httpClient, cClient, dbName); - connector.addView(luceneSearchView); - connector.setResultLimit(DatabaseSettings.LUCENE_SEARCH_LIMIT); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); + Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + NouveauDesignDocument searchView = new NouveauDesignDocument(); + searchView.setId(DDOC_NAME); + searchView.addNouveau(luceneSearchView, gson); + connector.addDesignDoc(searchView); } - public List search(String text, final Map > subQueryRestrictions ){ - return connector.searchViewWithRestrictions(Component.class, luceneSearchView, text, subQueryRestrictions); + public List search(String text, final Map> subQueryRestrictions ){ + return connector.searchViewWithRestrictions(Component.class, luceneSearchView.getIndexName(), + text, subQueryRestrictions); } - public List searchAccessibleComponents(String text, final Map > subQueryRestrictions, User user ){ - List resultComponentList = connector.searchViewWithRestrictions(Component.class, luceneSearchView, text, subQueryRestrictions); + public List searchAccessibleComponents(String text, final Map> subQueryRestrictions, User user ){ + List resultComponentList = connector.searchViewWithRestrictions(Component.class, + luceneSearchView.getIndexName(), text, subQueryRestrictions); List componentList = new ArrayList(); for (Component component : resultComponentList) { if (makePermission(component, user).isActionAllowed(RequestedAction.READ)) { @@ -118,12 +109,13 @@ public List searchAccessibleComponents(String text, final Map searchWithAccessibility(String text, final Map > subQueryRestrictions, User user ){ - List resultComponentList = connector.searchViewWithRestrictions(Component.class, luceneSearchView, text, subQueryRestrictions); + public List searchWithAccessibility(String text, final Map> subQueryRestrictions, + User user ){ + List resultComponentList = connector.searchViewWithRestrictions(Component.class, + luceneSearchView.getIndexName(), text, subQueryRestrictions); for (Component component : resultComponentList) { makePermission(component, user).fillPermissionsInOther(component); } return resultComponentList; } - } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java index 9296a3dceb..f95c3b049b 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java @@ -9,76 +9,75 @@ */ package org.eclipse.sw360.datahandler.db; +import com.cloudant.client.api.CloudantClient; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; +import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; +import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; +import org.ektorp.http.HttpClient; + import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Supplier; -import org.eclipse.sw360.datahandler.common.DatabaseSettings; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; -import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; -import org.ektorp.http.HttpClient; - -import com.cloudant.client.api.CloudantClient; +import static org.eclipse.sw360.common.utils.SearchUtils.OBJ_ARRAY_TO_STRING_INDEX; +import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; public class ModerationSearchHandler { - private static final LuceneSearchView luceneSearchView = new LuceneSearchView("lucene", "moderations", - "function(doc) {" + - " var ret = new Document();" + - " if(!doc.type) return ret;" + - " if(doc.type != 'moderation') return ret;" + - " if(!doc.documentId) return ret;" + - " function idx(obj) {" + - " for (var key in obj) {" + - " switch (typeof obj[key]) {" + - " case 'object':" + - " idx(obj[key]);" + - " break;" + - " case 'function':" + - " break;" + - " default:" + - " ret.add(obj[key]);" + - " break;" + - " }" + - " }" + - " };" + - " idx(doc);" + - " for(var i in doc.moderators) { "+ - " ret.add(doc.moderators[i], {\"field\": \"moderators\"} );" + - " }" + - " if(doc.documentName) { "+ - " ret.add(doc.documentName, {\"field\": \"documentName\"} );" + - " }" + - " if(doc.componentType) { "+ - " ret.add(doc.componentType, {\"field\": \"componentType\"} );" + - " }" + - " if(doc.requestingUser) { "+ - " ret.add(doc.requestingUser, {\"field\": \"requestingUser\"} );" + - " }" + - " if(doc.requestingUserDepartment) { "+ - " ret.add(doc.requestingUserDepartment, {\"field\": \"requestingUserDepartment\"} );" + - " }" + - " if(doc.moderationState) { "+ - " ret.add(doc.moderationState, {\"field\": \"moderationState\"} );" + - " }" + - " if(doc.timestamp) { "+ - " var dt = new Date(doc.timestamp); "+ - " var formattedDt = dt.getFullYear()+'-'+(dt.getMonth()+1)+'-'+dt.getDate(); "+ - " ret.add(formattedDt, {\"field\": \"timestamp\", \"type\": \"date\"} );" + - " }" + - " return ret;" + - "}"); - private final LuceneAwareDatabaseConnector connector; + + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + + private static final NouveauIndexDesignDocument luceneSearchView + = new NouveauIndexDesignDocument("moderations", + new NouveauIndexFunction( + "function(doc) {" + + OBJ_ARRAY_TO_STRING_INDEX + + " if(!doc.type || doc.type != 'moderation' || !doc.documentId) return;" + + " arrayToStringIndex(doc.moderators, 'moderators');" + + " if(doc.documentName && typeof(doc.documentName) == 'string' && doc.documentName.length > 0) {" + + " index('text', 'documentName', doc.documentName, {'store': true});" + + " }" + + " if(doc.documentType && typeof(doc.documentType) == 'string' && doc.documentType.length > 0) {" + + " index('text', 'documentType', doc.documentType, {'store': true});" + + " }" + + " if(doc.componentType && typeof(doc.componentType) == 'string' && doc.componentType.length > 0) {" + + " index('text', 'componentType', doc.componentType, {'store': true});" + + " }" + + " if(doc.requestingUser && typeof(doc.requestingUser) == 'string' && doc.requestingUser.length > 0) {" + + " index('text', 'requestingUser', doc.requestingUser, {'store': true});" + + " }" + + " if(doc.requestingUserDepartment && typeof(doc.requestingUserDepartment) == 'string' && doc.requestingUserDepartment.length > 0) {" + + " index('text', 'requestingUserDepartment', doc.requestingUserDepartment, {'store': true});" + + " }" + + " if(doc.moderationState && typeof(doc.moderationState) == 'string' && doc.moderationState.length > 0) {" + + " index('text', 'moderationState', doc.moderationState, {'store': true});" + + " }" + + " if(doc.timestamp) {"+ + " var dt = new Date(doc.timestamp); "+ + " var formattedDt = `${dt.getFullYear()}${(dt.getMonth()+1).toString().padStart(2,'0')}${dt.getDate().toString().padStart(2,'0')}`;" + + " index('double', 'timestamp', Number(formattedDt), {'store': true});"+ + " }" + + "}")); + private final NouveauLuceneAwareDatabaseConnector connector; public ModerationSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { - connector = new LuceneAwareDatabaseConnector(httpClient, cClient, dbName); - connector.addView(luceneSearchView); - connector.setResultLimit(DatabaseSettings.LUCENE_SEARCH_LIMIT); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); + Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + NouveauDesignDocument searchView = new NouveauDesignDocument(); + searchView.setId(DDOC_NAME); + searchView.addNouveau(luceneSearchView, gson); + connector.addDesignDoc(searchView); } - public List search(String text, final Map > subQueryRestrictions ) { - return connector.searchViewWithRestrictions(ModerationRequest.class, luceneSearchView, text, subQueryRestrictions); + public List search(String text, final Map> subQueryRestrictions ) { + return connector.searchViewWithRestrictions(ModerationRequest.class, luceneSearchView.getIndexName(), + text, subQueryRestrictions); } } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java index 646dda655a..d1dea9bdcc 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java @@ -10,44 +10,62 @@ */ package org.eclipse.sw360.datahandler.db; +import com.cloudant.client.api.CloudantClient; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettings; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.licenses.ObligationElement; +import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; import org.ektorp.http.HttpClient; -import com.cloudant.client.api.CloudantClient; - import java.io.IOException; import java.util.List; import java.util.function.Supplier; -import static org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; public class ObligationElementSearchHandler { - private static final LuceneSearchView luceneSearchView = new LuceneSearchView("lucene", "obligationelements", - "function(doc) {" + - " if(doc.type == 'obligationElement') { " + - " var ret = new Document();" + - " ret.add(doc.langElement); " + - " ret.add(doc.action); " + - " ret.add(doc.object); " + - " return ret;" + - " }" + - "}"); + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; - private final LuceneAwareDatabaseConnector connector; + private static final NouveauIndexDesignDocument luceneSearchView + = new NouveauIndexDesignDocument("obligationelements", + new NouveauIndexFunction( + "function(doc) {" + + " if(doc.type == 'obligationElement') {" + + " if (doc.langElement && typeof(doc.langElement) == 'string' && doc.langElement.length > 0) {" + + " index('text', 'langElement', doc.langElement, {'store': true});" + + " }" + + " if (doc.action && typeof(doc.action) == 'string' && doc.action.length > 0) {" + + " index('text', 'action', doc.action, {'store': true});" + + " }" + + " if (doc.object && typeof(doc.object) == 'string' && doc.object.length > 0) {" + + " index('text', 'object', doc.object, {'store': true});" + + " }" + + " }" + + "}")); - public ObligationElementSearchHandler(Supplier httpClient, Supplier cCLient, String dbName) throws IOException { + private final NouveauLuceneAwareDatabaseConnector connector; + + public ObligationElementSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); // Creates the database connector and adds the lucene search view - connector = new LuceneAwareDatabaseConnector(httpClient, cCLient, dbName); - connector.addView(luceneSearchView); + connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); + Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + NouveauDesignDocument searchView = new NouveauDesignDocument(); + searchView.setId(DDOC_NAME); + searchView.addNouveau(luceneSearchView, gson); + connector.addDesignDoc(searchView); connector.setResultLimit(DatabaseSettings.LUCENE_SEARCH_LIMIT); } public List search(String searchText) { - return connector.searchView(ObligationElement.class, luceneSearchView, prepareWildcardQuery(searchText)); + return connector.searchView(ObligationElement.class, luceneSearchView.getIndexName(), + prepareWildcardQuery(searchText)); } - } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java index 47a472d530..dd43dd66b9 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java @@ -11,59 +11,57 @@ package org.eclipse.sw360.datahandler.db; import com.cloudant.client.api.CloudantClient; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettings; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.licenses.Obligation; +import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; import org.ektorp.http.HttpClient; import java.io.IOException; import java.util.List; import java.util.function.Supplier; -import static org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; public class ObligationSearchHandler { - private static final LuceneSearchView luceneSearchView = new LuceneSearchView("lucene", "obligations", - "function(doc) {" + - " var ret = new Document();" + - " if(!doc.type) return ret;" + - " if(doc.type != 'obligation') return ret;" + - " function idx(obj) {" + - " for (var key in obj) {" + - " switch (typeof obj[key]) {" + - " case 'object':" + - " idx(obj[key]);" + - " break;" + - " case 'function':" + - " break;" + - " default:" + - " ret.add(obj[key]);" + - " break;" + - " }" + - " }" + - " };" + - " idx(doc);" + - " if(doc.title !== undefined && doc.title != null && doc.title.length >0) { "+ - " ret.add(doc.title, {\"field\": \"title\"} );" + - " }" + - " if(doc.text !== undefined && doc.text != null && doc.text.length >0) { "+ - " ret.add(doc.text, {\"field\": \"text\"} );" + - " }" + - " return ret;" + - "}"); + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; - private final LuceneAwareDatabaseConnector connector; + private static final NouveauIndexDesignDocument luceneSearchView + = new NouveauIndexDesignDocument("obligations", + new NouveauIndexFunction( + "function(doc) {" + + " if(!doc.type || doc.type != 'obligation') return;" + + " if(doc.title !== undefined && doc.title != null && doc.title.length >0) {" + + " index('text', 'title', doc.title, {'store': true});" + + " }" + + " if(doc.text !== undefined && doc.text != null && doc.text.length >0) {" + + " index('text', 'text', doc.text, {'store': true});" + + " }" + + "}")); - public ObligationSearchHandler(Supplier httpClient, Supplier cCLient, String dbName) throws IOException { + private final NouveauLuceneAwareDatabaseConnector connector; + + public ObligationSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); // Creates the database connector and adds the lucene search view - connector = new LuceneAwareDatabaseConnector(httpClient, cCLient, dbName); - connector.addView(luceneSearchView); + connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); + Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + NouveauDesignDocument searchView = new NouveauDesignDocument(); + searchView.setId(DDOC_NAME); + searchView.addNouveau(luceneSearchView, gson); + connector.addDesignDoc(searchView); connector.setResultLimit(DatabaseSettings.LUCENE_SEARCH_LIMIT); } public List search(String searchText) { - return connector.searchView(Obligation.class, luceneSearchView, prepareWildcardQuery(searchText)); + return connector.searchView(Obligation.class, luceneSearchView.getIndexName(), + prepareWildcardQuery(searchText)); } } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java index 5dd24f1fdc..333f247fe9 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java @@ -9,90 +9,89 @@ */ package org.eclipse.sw360.datahandler.db; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; +import com.cloudant.client.api.CloudantClient; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.packages.Package; +import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; import org.ektorp.http.HttpClient; -import com.cloudant.client.api.CloudantClient; - import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Supplier; -import static org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.common.utils.SearchUtils.OBJ_ARRAY_TO_STRING_INDEX; +import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; public class PackageSearchHandler { - private static final LuceneSearchView luceneSearchView = new LuceneSearchView("lucene", "packages", - "function(doc) {" + - " var ret = new Document();" + - " if (!doc.type) return ret;" + - " if (doc.type != 'package') return ret;" + - " function idx(obj) {" + - " for (var key in obj) {" + - " switch (typeof obj[key]) {" + - " case 'object':" + - " idx(obj[key]);" + - " break;" + - " case 'function':" + - " break;" + - " default:" + - " ret.add(obj[key]);" + - " break;" + - " }" + - " }" + - " };" + - " idx(doc);" + - " if (doc.name) { "+ - " ret.add(doc.name, {\"field\": \"name\"} );" + - " }" + - " if (doc.version) { "+ - " ret.add(doc.version, {\"field\": \"version\"} );" + - " }" + - " if (doc.purl) { "+ - " ret.add(doc.purl, {\"field\": \"purl\"} );" + - " }" + - " if (doc.releaseId) { "+ - " ret.add(doc.releaseId, {\"field\": \"releaseId\"} );" + - " }" + - " if (doc.vcs) { "+ - " ret.add(doc.vcs, {\"field\": \"vcs\"} );" + - " }" + - " if (doc.packageManager) { "+ - " ret.add(doc.packageManager, {\"field\": \"packageManager\"} );" + - " }" + - " if (doc.packageType) { "+ - " ret.add(doc.packageType, {\"field\": \"packageType\"} );" + - " }" + - " if (doc.createdBy) { "+ - " ret.add(doc.createdBy, {\"field\": \"createdBy\"} );" + - " }" + - " if (doc.createdOn) { "+ - " ret.add(doc.createdOn, {\"field\": \"createdOn\", \"type\": \"date\"} );" + - " }" + - " for (var i in doc.licenseIds) {" + - " ret.add(doc.licenseIds[i], {\"field\": \"licenseIds\"} );" + - " }" + - " return ret;" + - "}"); + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + private static final NouveauIndexDesignDocument luceneSearchView + = new NouveauIndexDesignDocument("packages", + new NouveauIndexFunction( + "function(doc) {" + + OBJ_ARRAY_TO_STRING_INDEX + + " if (!doc.type || doc.type != 'package') return;" + + " if (doc.name && typeof(doc.name) == 'string' && doc.name.length > 0) {" + + " index('text', 'name', doc.name, {'store': true});"+ + " }" + + " if (doc.version && typeof(doc.version) == 'string' && doc.version.length > 0) {" + + " index('text', 'version', doc.version, {'store': true});"+ + " }" + + " if (doc.purl && typeof(doc.purl) == 'string' && doc.purl.length > 0) {" + + " index('text', 'purl', doc.purl, {'store': true});"+ + " }" + + " if (doc.releaseId && typeof(doc.releaseId) == 'string' && doc.releaseId.length > 0) {" + + " index('text', 'releaseId', doc.releaseId, {'store': true});"+ + " }" + + " if (doc.vcs && typeof(doc.vcs) == 'string' && doc.vcs.length > 0) {" + + " index('text', 'vcs', doc.vcs, {'store': true});"+ + " }" + + " if (doc.packageManager && typeof(doc.packageManager) == 'string' && doc.packageManager.length > 0) {" + + " index('text', 'packageManager', doc.packageManager, {'store': true});"+ + " }" + + " if (doc.packageType && typeof(doc.packageType) == 'string' && doc.packageType.length > 0) {" + + " index('text', 'packageType', doc.packageType, {'store': true});"+ + " }" + + " if (doc.createdBy && typeof(doc.createdBy) == 'string' && doc.createdBy.length > 0) {" + + " index('text', 'createdBy', doc.createdBy, {'store': true});"+ + " }" + + " if(doc.createdOn && doc.createdOn.length) {"+ + " var dt = new Date(doc.createdOn);"+ + " var formattedDt = `${dt.getFullYear()}${(dt.getMonth()+1).toString().padStart(2,'0')}${dt.getDate().toString().padStart(2,'0')}`;" + + " index('double', 'createdOn', Number(formattedDt), {'store': true});"+ + " }" + + " arrayToStringIndex(doc.licenseIds, 'licenseIds');" + + "}")); - private final LuceneAwareDatabaseConnector connector; - public PackageSearchHandler(Supplier httpClient, Supplier cloudantClient, String dbName) throws IOException { - connector = new LuceneAwareDatabaseConnector(httpClient, cloudantClient, dbName); - connector.addView(luceneSearchView); + private final NouveauLuceneAwareDatabaseConnector connector; + + public PackageSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); + Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + NouveauDesignDocument searchView = new NouveauDesignDocument(); + searchView.setId(DDOC_NAME); + searchView.addNouveau(luceneSearchView, gson); + connector.addDesignDoc(searchView); } public List searchPackagesWithRestrictions(String text, final Map> subQueryRestrictions) { - return connector.searchViewWithRestrictions(Package.class, luceneSearchView, text, subQueryRestrictions); + return connector.searchViewWithRestrictions(Package.class, luceneSearchView.getIndexName(), + text, subQueryRestrictions); } public List searchPackages(String searchText) { - return connector.searchView(Package.class, luceneSearchView, prepareWildcardQuery(searchText)); + return connector.searchView(Package.class, luceneSearchView.getIndexName(), + prepareWildcardQuery(searchText)); } - } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java index 03c49e1446..8ab7742a82 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java @@ -9,101 +9,104 @@ */ package org.eclipse.sw360.datahandler.db; +import com.cloudant.client.api.CloudantClient; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettings; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.projects.Project; import org.eclipse.sw360.datahandler.thrift.users.User; +import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; import org.ektorp.http.HttpClient; -import com.cloudant.client.api.CloudantClient; - import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Collections; import java.util.function.Supplier; import java.util.stream.Collectors; -import static org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.common.utils.SearchUtils.OBJ_ARRAY_TO_STRING_INDEX; +import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; public class ProjectSearchHandler { - private static final LuceneSearchView luceneSearchView = new LuceneSearchView("lucene", "projects", - "function(doc) {" + - " var ret = new Document();" + - " if(!doc.type) return ret;" + - " if(doc.type != 'project') return ret;" + - " function idx(obj) {" + - " for (var key in obj) {" + - " switch (typeof obj[key]) {" + - " case 'object':" + - " idx(obj[key]);" + - " break;" + - " case 'function':" + - " break;" + - " default:" + - " ret.add(obj[key]);" + - " break;" + - " }" + - " }" + - " };" + - " idx(doc);" + - " if(doc.businessUnit !== undefined && doc.businessUnit != null && doc.businessUnit.length >0) { "+ - " ret.add(doc.businessUnit, {\"field\": \"businessUnit\"} );" + - " }" + - " if(doc.projectType !== undefined && doc.projectType != null && doc.projectType.length >0) { "+ - " ret.add(doc.projectType, {\"field\": \"projectType\"} );" + - " }" + - " if(doc.projectResponsible !== undefined && doc.projectResponsible != null && doc.projectResponsible.length >0) { "+ - " ret.add(doc.projectResponsible, {\"field\": \"projectResponsible\"} );" + - " }" + - " if(doc.name !== undefined && doc.name != null && doc.name.length >0) { "+ - " ret.add(doc.name, {\"field\": \"name\"} );" + - " }" + - " if(doc.version !== undefined && doc.version != null && doc.version.length >0) { "+ - " ret.add(doc.version, {\"field\": \"version\"} );" + - " }" + - " if(doc.state !== undefined && doc.state != null && doc.state.length >0) { "+ - " ret.add(doc.state, {\"field\": \"state\"} );" + - " }" + - " if(doc.clearingState) { "+ - " ret.add(doc.clearingState, {\"field\": \"clearingState\"} );" + - " }" + - " if(doc.tag !== undefined && doc.tag != null && doc.tag.length >0) { "+ - " ret.add(doc.tag, {\"field\": \"tag\"} );" + - " }" + - " for(var [key, value] in doc.additionalData) {" + - " ret.add(doc.additionalData[key], {\"field\": \"additionalData\"} );" + - " }" + - " if(doc.releaseRelationNetwork !== undefined && doc.releaseRelationNetwork != null && doc.releaseRelationNetwork.length > 0) { "+ - " ret.add(doc.releaseRelationNetwork, {\"field\": \"releaseRelationNetwork\"} );" + - " }" + - " return ret;" + - "}"); + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + + private static final NouveauIndexDesignDocument luceneSearchView + = new NouveauIndexDesignDocument("projects", + new NouveauIndexFunction( + "function(doc) {" + + OBJ_ARRAY_TO_STRING_INDEX + + " if(!doc.type || doc.type != 'project') return;" + + " if(doc.businessUnit !== undefined && doc.businessUnit != null && doc.businessUnit.length >0) {" + + " index('text', 'businessUnit', doc.businessUnit, {'store': true});" + + " }" + + " if(doc.projectType !== undefined && doc.projectType != null && doc.projectType.length >0) {" + + " index('text', 'projectType', doc.projectType, {'store': true});" + + " }" + + " if(doc.projectResponsible !== undefined && doc.projectResponsible != null && doc.projectResponsible.length >0) {" + + " index('text', 'projectResponsible', doc.projectResponsible, {'store': true});" + + " }" + + " if(doc.name !== undefined && doc.name != null && doc.name.length >0) {" + + " index('text', 'name', doc.name, {'store': true});" + + " }" + + " if(doc.version !== undefined && doc.version != null && doc.version.length >0) {" + + " index('string', 'version', doc.version, {'store': true});" + + " }" + + " if(doc.state !== undefined && doc.state != null && doc.state.length >0) {" + + " index('text', 'state', doc.state, {'store': true});" + + " }" + + " if(doc.clearingState) {" + + " index('text', 'clearingState', doc.clearingState, {'store': true});" + + " }" + + " if(doc.tag !== undefined && doc.tag != null && doc.tag.length >0) {" + + " index('text', 'tag', doc.tag, {'store': true});" + + " }" + + " arrayToStringIndex(doc.additionalData, 'additionalData');" + + " if(doc.releaseRelationNetwork !== undefined && doc.releaseRelationNetwork != null && doc.releaseRelationNetwork.length > 0) {" + + " index('text', 'releaseRelationNetwork', doc.releaseRelationNetwork, {'store': true});" + + " }" + + "}") + .setFieldAnalyzer( + Map.of("version", "keyword") + ) + ); - private final LuceneAwareDatabaseConnector connector; + private final NouveauLuceneAwareDatabaseConnector connector; - public ProjectSearchHandler(Supplier httpClient, Supplier cCLient, String dbName) throws IOException { - connector = new LuceneAwareDatabaseConnector(httpClient, cCLient, dbName); - connector.addView(luceneSearchView); + public ProjectSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); + Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + NouveauDesignDocument searchView = new NouveauDesignDocument(); + searchView.setId(DDOC_NAME); + searchView.addNouveau(luceneSearchView, gson); connector.setResultLimit(DatabaseSettings.LUCENE_SEARCH_LIMIT); + connector.addDesignDoc(searchView); } - public List search(String text, final Map > subQueryRestrictions, User user ){ - return connector.searchProjectViewWithRestrictionsAndFilter(luceneSearchView, text, subQueryRestrictions, user); + public List search(String text, final Map> subQueryRestrictions, User user) { + return connector.searchProjectViewWithRestrictionsAndFilter(luceneSearchView.getIndexName(), text, + subQueryRestrictions, user); } public List search(String searchText) { - return connector.searchView(Project.class, luceneSearchView, prepareWildcardQuery(searchText)); + return connector.searchView(Project.class, luceneSearchView.getIndexName(), + prepareWildcardQuery(searchText)); } - public List search(String text, final Map> subQueryRestrictions) { - return connector.searchViewWithRestrictions(Project.class, luceneSearchView, text, subQueryRestrictions); + public List search(String text, final Map> subQueryRestrictions) { + return connector.searchViewWithRestrictions(Project.class, luceneSearchView.getIndexName(), + text, subQueryRestrictions); } public Set searchByReleaseId(String id, User user) { @@ -114,9 +117,11 @@ public Set searchByReleaseIds(Set ids, User user) { Map> filterMap = getFilterMapForSetReleaseIds(ids); List projectsByReleaseIds; if (user != null) { - projectsByReleaseIds = connector.searchProjectViewWithRestrictionsAndFilter(luceneSearchView, null, filterMap, user); + projectsByReleaseIds = connector.searchProjectViewWithRestrictionsAndFilter(luceneSearchView.getIndexName(), + null, filterMap, user); } else { - projectsByReleaseIds = connector.searchViewWithRestrictions(Project.class, luceneSearchView, null, filterMap); + projectsByReleaseIds = connector.searchViewWithRestrictions(Project.class, luceneSearchView.getIndexName(), + null, filterMap); } return new HashSet<>(projectsByReleaseIds); } @@ -128,7 +133,7 @@ private static Map> getFilterMapForSetReleaseIds(Set values.add("\"releaseId\":\"" + releaseId + "\""); values.add("\"releaseId\": \"" + releaseId + "\""); } - values = values.stream().map(LuceneAwareDatabaseConnector::prepareWildcardQuery).collect(Collectors.toSet()); + values = values.stream().map(NouveauLuceneAwareDatabaseConnector::prepareWildcardQuery).collect(Collectors.toSet()); filterMap.put(Project._Fields.RELEASE_RELATION_NETWORK.getFieldName(), values); return filterMap; } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java index 7e248f14a4..b44f5caeee 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java @@ -9,18 +9,23 @@ */ package org.eclipse.sw360.datahandler.db; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; +import com.cloudant.client.api.CloudantClient; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.components.Release; +import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; import org.ektorp.http.HttpClient; -import com.cloudant.client.api.CloudantClient; - import java.io.IOException; import java.util.List; import java.util.function.Supplier; -import static org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; /** * Lucene search for the Release class @@ -29,26 +34,36 @@ */ public class ReleaseSearchHandler { - private static final LuceneSearchView luceneSearchView - = new LuceneSearchView("lucene", "releases", - "function(doc) {" + - " if(doc.type == 'release') { " + - " var ret = new Document();" + - " ret.add(doc.name); " + - " ret.add(doc.version); " + - " ret.add(doc._id); " + - " return ret;" + - " }" + - "}"); + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + + private static final NouveauIndexDesignDocument luceneSearchView + = new NouveauIndexDesignDocument("releases", + new NouveauIndexFunction( + "function(doc) {" + + " if(doc.type == 'release') {" + + " if (doc.name && typeof(doc.name) == 'string' && doc.name.length > 0) {" + + " index('text', 'name', doc.name, {'store': true});" + + " }" + + " if (doc.version && typeof(doc.version) == 'string' && doc.version.length > 0) {" + + " index('text', 'version', doc.version, {'store': true});" + + " }" + + " index('text', 'id', doc._id, {'store': true});" + + " }" + + "}")); - private final LuceneAwareDatabaseConnector connector; + private final NouveauLuceneAwareDatabaseConnector connector; public ReleaseSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { - connector = new LuceneAwareDatabaseConnector(httpClient, cClient, dbName); - connector.addView(luceneSearchView); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); + Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + NouveauDesignDocument searchView = new NouveauDesignDocument(); + searchView.setId(DDOC_NAME); + searchView.addNouveau(luceneSearchView, gson); + connector.addDesignDoc(searchView); } public List search(String searchText) { - return connector.searchView(Release.class, luceneSearchView, prepareWildcardQuery(searchText)); + return connector.searchView(Release.class, luceneSearchView.getIndexName(), prepareWildcardQuery(searchText)); } } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java index d66bbdeb5c..9817c44a16 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java @@ -9,12 +9,15 @@ */ package org.eclipse.sw360.datahandler.db; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; -import org.eclipse.sw360.datahandler.thrift.users.User; - import com.cloudant.client.api.CloudantClient; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; +import org.eclipse.sw360.datahandler.thrift.users.User; +import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; import java.io.IOException; import java.util.List; @@ -22,72 +25,67 @@ import java.util.Set; import java.util.function.Supplier; -import static org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector.prepareFuzzyQuery; +import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareFuzzyQuery; +import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; public class UserSearchHandler { - private static final LuceneSearchView luceneSearchView - = new LuceneSearchView("lucene", "users", - "function(doc) {" + - " if(doc.type == 'user') { " + - " var ret = new Document();" + - " ret.add(doc.givenname); " + - " ret.add(doc.lastname); " + - " ret.add(doc.email); " + - " return ret;" + - " }" + - "}"); + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + + private static final NouveauIndexDesignDocument luceneSearchView + = new NouveauIndexDesignDocument("users", + new NouveauIndexFunction("function(doc) {" + + " if (doc.type == 'user') { " + + " if (doc.givenname && typeof(doc.givenname) == 'string' && doc.givenname.length > 0) {" + + " index('text', 'givenname', doc.givenname, {'store': true});" + + " }" + + " if (doc.lastname && typeof(doc.lastname) == 'string' && doc.lastname.length > 0) {" + + " index('text', 'lastname', doc.lastname, {'store': true});" + + " }" + + " if (doc.email && typeof(doc.email) == 'string' && doc.email.length > 0) {" + + " index('text', 'email', doc.email, {'store': true});" + + " }" + + " }" + + "}")); - private static final LuceneSearchView luceneUserSearchView = new LuceneSearchView("lucene", "usersearch", - "function(doc) {" + - " var ret = new Document();" + - " if (!doc.type) return ret;" + - " if (doc.type != 'user') return ret;" + - " function idx(obj) {" + - " for (var key in obj) {" + - " switch (typeof obj[key]) {" + - " case 'object':" + - " idx(obj[key]);" + - " break;" + - " case 'function':" + - " break;" + - " default:" + - " ret.add(obj[key]);" + - " break;" + - " }" + - " }" + - " };" + - " idx(doc);" + - " if (doc.givenname) { "+ - " ret.add(doc.givenname, {\"field\": \"givenname\"} );" + - " }" + - " if (doc.lastname) { "+ - " ret.add(doc.lastname, {\"field\": \"lastname\"} );" + - " }" + - " if (doc.email) { "+ - " ret.add(doc.email, {\"field\": \"email\"} );" + - " }" + - " if (doc.userGroup) { "+ - " ret.add(doc.userGroup, {\"field\": \"userGroup\"} );" + - " }" + - " if (doc.department) { "+ - " ret.add(doc.department, {\"field\": \"department\"} );" + - " }" + - " return ret;" + - "}"); + private static final NouveauIndexDesignDocument luceneUserSearchView + = new NouveauIndexDesignDocument("usersearch", + new NouveauIndexFunction("function(doc) {" + + " if (!doc.type || doc.type != 'user') return;" + + " if (doc.givenname && typeof(doc.givenname) == 'string' && doc.givenname.length > 0) {" + + " index('text', 'givenname', doc.givenname, {'store': true});" + + " }" + + " if (doc.lastname && typeof(doc.lastname) == 'string' && doc.lastname.length > 0) {" + + " index('text', 'lastname', doc.lastname, {'store': true});" + + " }" + + " if (doc.email && typeof(doc.email) == 'string' && doc.email.length > 0) {" + + " index('text', 'email', doc.email, {'store': true});" + + " }" + + " if (doc.userGroup && typeof(doc.userGroup) == 'string' && doc.userGroup.length > 0) {" + + " index('text', 'userGroup', doc.userGroup, {'store': true});" + + " }" + + " if (doc.department && typeof(doc.department) == 'string' && doc.department.length > 0) {" + + " index('text', 'department', doc.department, {'store': true});" + + " }" + + "}")); - private final LuceneAwareDatabaseConnector connector; + private final NouveauLuceneAwareDatabaseConnector connector; - public UserSearchHandler(DatabaseConnector databaseConnector, Supplier cClient) throws IOException { + public UserSearchHandler(Supplier cClient, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); // Creates the database connector and adds the lucene search view - connector = new LuceneAwareDatabaseConnector(databaseConnector, cClient); - connector.addView(luceneSearchView); - connector.addView(luceneUserSearchView); + connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); + Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + NouveauDesignDocument searchView = new NouveauDesignDocument(); + searchView.setId(DDOC_NAME); + searchView.addNouveau(luceneSearchView, gson); + searchView.addNouveau(luceneUserSearchView, gson); + connector.addDesignDoc(searchView); } private String cleanUp(String searchText) { // Lucene seems to split email addresses at an '@' when indexing - // so in this case we only search for the user name in front of the '@' + // so in this case we only search for the username in front of the '@' return searchText.split("@")[0]; } @@ -97,10 +95,10 @@ public List searchByNameAndEmail(String searchText) { searchText = ""; } String queryString = prepareFuzzyQuery(cleanUp(searchText)); - return connector.searchAndSortByScore(User.class, luceneSearchView, queryString); + return connector.searchAndSortByScore(User.class, luceneSearchView.getIndexName(), queryString); } public List search(String text, final Map> subQueryRestrictions) { - return connector.searchViewWithRestrictions(User.class, luceneUserSearchView, text, subQueryRestrictions); + return connector.searchViewWithRestrictions(User.class, luceneUserSearchView.getIndexName(), text, subQueryRestrictions); } } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java index c03689fa05..8fb07f05b4 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java @@ -9,18 +9,23 @@ */ package org.eclipse.sw360.datahandler.db; +import com.cloudant.client.api.CloudantClient; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; - -import com.cloudant.client.api.CloudantClient; +import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; import java.io.IOException; import java.util.List; import java.util.function.Supplier; -import static org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; /** * Lucene search for the Vendor class @@ -31,32 +36,44 @@ */ public class VendorSearchHandler { - private static final LuceneSearchView luceneSearchView - = new LuceneSearchView("lucene", "vendors", - "function(doc) {" + - " if(doc.type == 'vendor') { " + - " var ret = new Document();" + - " ret.add(doc.shortname); " + - " ret.add(doc.fullname); " + - " return ret;" + - " }" + - "}"); + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + + private static final NouveauIndexDesignDocument luceneSearchView + = new NouveauIndexDesignDocument("vendors", + new NouveauIndexFunction( + "function(doc) {" + + " if(doc.type == 'vendor') {" + + " if (typeof(doc.shortname) == 'string' && doc.shortname.length > 0) {" + + " index('text', 'shortname', doc.shortname, {'store': true});" + + " }" + + " if (typeof(doc.fullname) == 'string' && doc.fullname.length > 0) {" + + " index('text', 'fullname', doc.fullname, {'store': true});" + + " }" + + " }" + + "}")); - private final LuceneAwareDatabaseConnector connector; + private final NouveauLuceneAwareDatabaseConnector connector; public VendorSearchHandler(DatabaseConnector databaseConnector, Supplier cClient) throws IOException { // Creates the database connector and adds the lucene search view - connector = new LuceneAwareDatabaseConnector(databaseConnector, cClient); - connector.addView(luceneSearchView); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, databaseConnector.getDbName()); + connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); + Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + NouveauDesignDocument searchView = new NouveauDesignDocument(); + searchView.setId(DDOC_NAME); + searchView.addNouveau(luceneSearchView, gson); + connector.addDesignDoc(searchView); } public List search(String searchText) { // Query the search view for the provided text - return connector.searchView(Vendor.class, luceneSearchView, prepareWildcardQuery(searchText)); + return connector.searchView(Vendor.class, luceneSearchView.getIndexName(), + prepareWildcardQuery(searchText)); } public List searchIds(String searchText) { // Query the search view for the provided text - return connector.searchIds(Vendor.class, luceneSearchView, prepareWildcardQuery(searchText)); + return connector.searchIds(Vendor.class, luceneSearchView.getIndexName(), + prepareWildcardQuery(searchText)); } } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java index f440bf53a2..0a46d28830 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java @@ -10,39 +10,51 @@ */ package org.eclipse.sw360.datahandler.db.spdx.document; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; +import com.cloudant.client.api.CloudantClient; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; import org.ektorp.http.HttpClient; -import com.cloudant.client.api.CloudantClient; - import java.io.IOException; import java.util.List; import java.util.function.Supplier; -import static org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; public class SpdxDocumentSearchHandler { - private static final LuceneSearchView luceneSearchView - = new LuceneSearchView("lucene", "SPDXDocument", - "function(doc) {" + - " if(doc.type == 'SPDXDocument') { " + - " var ret = new Document();" + - " ret.add(doc._id); " + - " return ret;" + - " }" + - "}"); + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + + private static final NouveauIndexDesignDocument luceneSearchView + = new NouveauIndexDesignDocument("SPDXDocument", + new NouveauIndexFunction( + "function(doc) {" + + " if(doc.type == 'SPDXDocument') {" + + " index('text', 'id', doc._id, {'store': true});" + + " }" + + "}")); - private final LuceneAwareDatabaseConnector connector; + private final NouveauLuceneAwareDatabaseConnector connector; public SpdxDocumentSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { - connector = new LuceneAwareDatabaseConnector(httpClient, cClient, dbName); - connector.addView(luceneSearchView); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); + Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + NouveauDesignDocument searchView = new NouveauDesignDocument(); + searchView.setId(DDOC_NAME); + searchView.addNouveau(luceneSearchView, gson); + connector.addDesignDoc(searchView); } public List search(String searchText) { - return connector.searchView(SPDXDocument.class, luceneSearchView, prepareWildcardQuery(searchText)); + return connector.searchView(SPDXDocument.class, luceneSearchView.getIndexName(), + prepareWildcardQuery(searchText)); } } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java index d863e38f6f..3585b5db93 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java @@ -10,39 +10,51 @@ */ package org.eclipse.sw360.datahandler.db.spdx.documentcreationinfo; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; +import com.cloudant.client.api.CloudantClient; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; +import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; import org.ektorp.http.HttpClient; -import com.cloudant.client.api.CloudantClient; - import java.io.IOException; import java.util.List; import java.util.function.Supplier; -import static org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; public class SpdxDocumentCreationInfoSearchHandler { - private static final LuceneSearchView luceneSearchView - = new LuceneSearchView("lucene", "documentCreationInformation", - "function(doc) {" + - " if(doc.type == 'documentCreationInformation') { " + - " var ret = new Document();" + - " ret.add(doc._id); " + - " return ret;" + - " }" + - "}"); + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + + private static final NouveauIndexDesignDocument luceneSearchView + = new NouveauIndexDesignDocument("documentCreationInformation", + new NouveauIndexFunction( + "function(doc) {" + + " if(doc.type == 'documentCreationInformation') {" + + " index('text', 'id', doc._id, {'store': true});" + + " }" + + "}")); - private final LuceneAwareDatabaseConnector connector; + private final NouveauLuceneAwareDatabaseConnector connector; public SpdxDocumentCreationInfoSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { - connector = new LuceneAwareDatabaseConnector(httpClient, cClient, dbName); - connector.addView(luceneSearchView); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); + Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + NouveauDesignDocument searchView = new NouveauDesignDocument(); + searchView.setId(DDOC_NAME); + searchView.addNouveau(luceneSearchView, gson); + connector.addDesignDoc(searchView); } public List search(String searchText) { - return connector.searchView(DocumentCreationInformation.class, luceneSearchView, prepareWildcardQuery(searchText)); + return connector.searchView(DocumentCreationInformation.class, luceneSearchView.getIndexName(), + prepareWildcardQuery(searchText)); } } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java index 70b5b2d61f..a9dc71a001 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java @@ -10,39 +10,51 @@ */ package org.eclipse.sw360.datahandler.db.spdx.packageinfo; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; -import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.*; -import org.ektorp.http.HttpClient; - import com.cloudant.client.api.CloudantClient; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; +import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; +import org.ektorp.http.HttpClient; import java.io.IOException; import java.util.List; import java.util.function.Supplier; -import static org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; public class SpdxPackageInfoSearchHandler { - private static final LuceneSearchView luceneSearchView - = new LuceneSearchView("lucene", "packageInformation", - "function(doc) {" + - " if(doc.type == 'packageInformation') { " + - " var ret = new Document();" + - " ret.add(doc._id); " + - " return ret;" + - " }" + - "}"); + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + + private static final NouveauIndexDesignDocument luceneSearchView + = new NouveauIndexDesignDocument("packageInformation", + new NouveauIndexFunction( + "function(doc) {" + + " if(doc.type == 'packageInformation') { " + + " index('text', 'id', doc._id, {'store': true});" + + " }" + + "}")); - private final LuceneAwareDatabaseConnector connector; + private final NouveauLuceneAwareDatabaseConnector connector; public SpdxPackageInfoSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { - connector = new LuceneAwareDatabaseConnector(httpClient, cClient, dbName); - connector.addView(luceneSearchView); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); + Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + NouveauDesignDocument searchView = new NouveauDesignDocument(); + searchView.setId(DDOC_NAME); + searchView.addNouveau(luceneSearchView, gson); + connector.addDesignDoc(searchView); } public List search(String searchText) { - return connector.searchView(PackageInformation.class, luceneSearchView, prepareWildcardQuery(searchText)); + return connector.searchView(PackageInformation.class, luceneSearchView.getIndexName(), + prepareWildcardQuery(searchText)); } } diff --git a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java index 32e528bae4..b812708a01 100644 --- a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java +++ b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java @@ -11,23 +11,31 @@ package org.eclipse.sw360.search.db; import com.cloudant.client.api.CloudantClient; -import com.github.ldriscoll.ektorplucene.LuceneResult; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.collect.FluentIterable; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettings; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.search.SearchResult; import org.eclipse.sw360.datahandler.thrift.users.User; +import org.eclipse.sw360.nouveau.NouveauResult; +import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; +import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; import org.ektorp.http.HttpClient; +import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; -import static org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.common.utils.SearchUtils.OBJ_TO_DEFAULT_INDEX; +import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; +import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; /** * Class for accessing the Lucene connector on the CouchDB database @@ -36,75 +44,71 @@ */ public abstract class AbstractDatabaseSearchHandler { - private static final LuceneSearchView luceneSearchView = new LuceneSearchView("lucene", "all", + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + + private static final NouveauIndexDesignDocument luceneSearchView + = new NouveauIndexDesignDocument("all", new NouveauIndexFunction( "function(doc) {" + - " var ret = new Document();" + - " if(!doc.type) return ret;" + - " function idx(obj) {" + - " for (var key in obj) {" + - " switch (typeof obj[key]) {" + - " case 'object':" + - " idx(obj[key]);" + - " break;" + - " case 'function':" + - " break;" + - " default:" + - " ret.add(obj[key]);" + - " break;" + - " }" + - " }" + - " };" + - " idx(doc);" + - " ret.add(doc.type, {\"field\": \"type\"} );" + - " return ret;" + - "}"); - private static final LuceneSearchView luceneFilteredSearchView = new LuceneSearchView("lucene", "restrictedSearch", + " if(!doc.type) return;" + + OBJ_TO_DEFAULT_INDEX + + " var objString = getObjAsString(doc);" + + " if (objString && objString.length > 0) {" + + " index('text', 'default', objString, {'store': true});" + + " }" + + " if (doc.type && typeof(doc.type) == 'string' && doc.type.length > 0) {" + + " index('text', 'type', doc.type, {'store': true});" + + " }" + + "}")); + + private static final NouveauIndexDesignDocument luceneFilteredSearchView + = new NouveauIndexDesignDocument("restrictedSearch", new NouveauIndexFunction( "function(doc) {" + - " var ret = new Document();" + - " if(!doc.type) return ret;" + - " function idx(obj) {" + - " for (var key in obj) {" + - " switch (typeof obj[key]) {" + - " case 'object':" + - " idx(obj[key]);" + - " break;" + - " case 'function':" + - " break;" + - " default:" + - " ret.add(obj[key]);" + - " break;" + - " }" + - " }" + - " };" + - " idx(doc);" + - " ret.add(doc.type, {\"field\": \"type\"} );" + - " if(doc.name && doc.name.length > 0) { "+ - " ret.add(doc.name, {\"field\": \"name\"} );" + - " }" + - " if (doc.fullname && doc.fullname.length > 0) { "+ - " ret.add(doc.fullname, {\"field\": \"fullname\"} );" + - " }" + - " if (doc.title && doc.title.length > 0) { "+ - " ret.add(doc.title, {\"field\": \"title\"} );" + - " }" + - " return ret;" + - "}"); - private final LuceneAwareDatabaseConnector connector; + " if(!doc.type) return;" + + OBJ_TO_DEFAULT_INDEX + + " var objString = getObjAsString(doc);" + + " if (objString && objString.length > 0) {" + + " index('text', 'default', objString, {'store': true});" + + " }" + + " if (doc.type && typeof(doc.type) == 'string' && doc.type.length > 0) {" + + " index('text', 'type', doc.type, {'store': true});" + + " }" + + " if (doc.name && typeof(doc.name) == 'string' && doc.name.length > 0) {" + + " index('text', 'name', doc.name, {'store': true});" + + " }" + + " if (doc.fullname && typeof(doc.fullname) == 'string' && doc.fullname.length > 0) {" + + " index('text', 'fullname', doc.fullname, {'store': true});" + + " }" + + " if (doc.title && typeof(doc.title) == 'string' && doc.title.length > 0) {" + + " index('text', 'title', doc.title, {'store': true});" + + " }" + + "}")); + private final NouveauLuceneAwareDatabaseConnector connector; public AbstractDatabaseSearchHandler(String dbName) throws IOException { + Supplier cClient = DatabaseSettings.getConfiguredClient(); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); // Create the database connector and add the search view to couchDB - connector = new LuceneAwareDatabaseConnector(DatabaseSettings.getConfiguredHttpClient(), DatabaseSettings.getConfiguredClient(), dbName); - connector.addView(luceneSearchView); - connector.addView(luceneFilteredSearchView); + connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); + Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + NouveauDesignDocument searchView = new NouveauDesignDocument(); + searchView.setId(DDOC_NAME); + searchView.addNouveau(luceneSearchView, gson); + searchView.addNouveau(luceneFilteredSearchView, gson); connector.setResultLimit(DatabaseSettings.LUCENE_SEARCH_LIMIT); + connector.addDesignDoc(searchView); } - public AbstractDatabaseSearchHandler(Supplier client, Supplier cclient, String dbName) throws IOException { + public AbstractDatabaseSearchHandler(Supplier client, Supplier cClient, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); // Create the database connector and add the search view to couchDB - connector = new LuceneAwareDatabaseConnector(client, cclient, dbName); - connector.addView(luceneSearchView); - connector.addView(luceneFilteredSearchView); + connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); + Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + NouveauDesignDocument searchView = new NouveauDesignDocument(); + searchView.setId(DDOC_NAME); + searchView.addNouveau(luceneSearchView, gson); + searchView.addNouveau(luceneFilteredSearchView, gson); connector.setResultLimit(DatabaseSettings.LUCENE_SEARCH_LIMIT); + connector.addDesignDoc(searchView); } /** @@ -177,22 +181,22 @@ public List restrictedSearch(String text, List typeMask, U return getFilteredSearchResults(query, user); } - private List getSearchResults(String queryString, User user) { - LuceneResult queryLucene = connector.searchView(luceneSearchView, queryString); + private @NotNull List getSearchResults(String queryString, User user) { + NouveauResult queryLucene = connector.searchView(luceneSearchView.getIndexName(), queryString); return convertLuceneResultAndFilterForVisibility(queryLucene, user); } - private List getFilteredSearchResults(String queryString, User user) { - LuceneResult queryLucene = connector.searchView(luceneFilteredSearchView, queryString); + private @NotNull List getFilteredSearchResults(String queryString, User user) { + NouveauResult queryLucene = connector.searchView(luceneFilteredSearchView.getIndexName(), queryString); return convertLuceneResultAndFilterForVisibility(queryLucene, user); } - private List convertLuceneResultAndFilterForVisibility(LuceneResult queryLucene, User user) { + private @NotNull List convertLuceneResultAndFilterForVisibility(NouveauResult queryLucene, User user) { List results = new ArrayList<>(); if (queryLucene != null) { - for (LuceneResult.Row row : queryLucene.getRows()) { - SearchResult result = makeSearchResult(row); - if (result != null && !result.getName().isEmpty() && isVisibleToUser(result, user)) { + for (NouveauResult.Hits hit : queryLucene.getHits()) { + SearchResult result = makeSearchResult(hit); + if (!result.getName().isEmpty() && isVisibleToUser(result, user)) { results.add(result); } } @@ -205,15 +209,15 @@ private List convertLuceneResultAndFilterForVisibility(LuceneResul /** * Transforms a LuceneResult row into a Thrift SearchResult object */ - private static SearchResult makeSearchResult(LuceneResult.Row row) { + private static @NotNull SearchResult makeSearchResult(@NotNull NouveauResult.Hits hit) { SearchResult result = new SearchResult(); // Set row properties - result.id = row.getId(); - result.score = row.getScore(); + result.id = hit.getId(); + result.score = hit.getScore(); // Get document and - SearchDocument parser = new SearchDocument(row.getDoc()); + SearchDocument parser = new SearchDocument(hit.getDoc()); // Get basic search results information result.type = parser.getType(); @@ -221,5 +225,4 @@ private static SearchResult makeSearchResult(LuceneResult.Row row) { return result; } - } diff --git a/backend/src/src-users/src/main/java/org/eclipse/sw360/users/db/UserDatabaseHandler.java b/backend/src/src-users/src/main/java/org/eclipse/sw360/users/db/UserDatabaseHandler.java index c9f4ff6d9b..15f2363447 100644 --- a/backend/src/src-users/src/main/java/org/eclipse/sw360/users/db/UserDatabaseHandler.java +++ b/backend/src/src-users/src/main/java/org/eclipse/sw360/users/db/UserDatabaseHandler.java @@ -75,15 +75,15 @@ public UserDatabaseHandler(Supplier httpClient, String dbName) t dbConnector = new DatabaseConnector(DatabaseSettings.getConfiguredHttpClient(), dbName); repository = new UserRepository(db); readFileDepartmentConfig = new ReadFileDepartmentConfig(); - userSearchHandler = new UserSearchHandler(dbConnector, httpClient); + userSearchHandler = new UserSearchHandler(DatabaseSettings.getConfiguredClient(), dbName); } - public UserDatabaseHandler(Supplier httpClient,Supplier client, String dbName) throws IOException { + public UserDatabaseHandler(Supplier httpClient, Supplier client, String dbName) throws IOException { // Create the connector db = new DatabaseConnectorCloudant(httpClient, dbName); dbConnector = new DatabaseConnector(client, dbName); repository = new UserRepository(db); - userSearchHandler = new UserSearchHandler(dbConnector, httpClient); + userSearchHandler = new UserSearchHandler(httpClient, dbName); } public User getByEmail(String email) { diff --git a/libraries/datahandler/pom.xml b/libraries/datahandler/pom.xml index c89ab01a55..d24838235c 100644 --- a/libraries/datahandler/pom.xml +++ b/libraries/datahandler/pom.xml @@ -209,6 +209,7 @@ com.cloudant cloudant-client + ${cloudant.version} com.fasterxml.jackson.core @@ -266,5 +267,11 @@ org.apache.maven maven-model + + org.eclipse.sw360 + nouveau-handler + ${project.version} + compile + diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java index 44714e17b7..79e563e426 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java @@ -329,4 +329,8 @@ public List getDocsByListIds(Class type, Collection ids) { return Collections.emptyList(); } } + + public Database getDatabase() { + return database; + } } diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseInstanceCloudant.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseInstanceCloudant.java index c28706cfb3..24d90042ae 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseInstanceCloudant.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseInstanceCloudant.java @@ -44,4 +44,8 @@ public void destroy() { public void deleteDatabase(String dbName) { client.deleteDB(dbName); } + + public CloudantClient getClient() { + return client; + } } diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Utils.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Utils.java index 239853e88d..7f6c7cab24 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Utils.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Utils.java @@ -26,7 +26,7 @@ import com.google.gson.Gson; import org.eclipse.sw360.datahandler.couchdb.DatabaseMixInForChangeLog.ProjectProjectRelationshipMixin; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.permissions.PermissionUtils; import org.eclipse.sw360.datahandler.thrift.*; import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; @@ -932,7 +932,7 @@ private static Map> getFilterMapForSetReleaseIds(Set values.add("\"releaseId\":\"" + releaseId + "\""); values.add("\"releaseId\": \"" + releaseId + "\""); } - values = values.stream().map(LuceneAwareDatabaseConnector::prepareWildcardQuery).collect(Collectors.toSet()); + values = values.stream().map(NouveauLuceneAwareDatabaseConnector::prepareWildcardQuery).collect(Collectors.toSet()); filterMap.put(Project._Fields.RELEASE_RELATION_NETWORK.getFieldName(), values); return filterMap; } diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/LuceneAwareDatabaseConnector.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/LuceneAwareDatabaseConnector.java deleted file mode 100644 index 016a408c6d..0000000000 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/LuceneAwareDatabaseConnector.java +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright Siemens AG, 2014-2018. Part of the SW360 Portal Project. - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.sw360.datahandler.couchdb.lucene; - -import com.github.ldriscoll.ektorplucene.EktorpLuceneObjectMapperFactory; -import com.github.ldriscoll.ektorplucene.LuceneAwareCouchDbConnector; -import com.github.ldriscoll.ektorplucene.LuceneQuery; -import com.github.ldriscoll.ektorplucene.LuceneResult; -import com.github.ldriscoll.ektorplucene.util.IndexUploader; -import com.google.common.base.Joiner; -import com.google.gson.Gson; - -import java.net.HttpURLConnection; -import java.net.URL; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.common.DatabaseSettings; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; -import org.eclipse.sw360.datahandler.permissions.ProjectPermissions; -import org.eclipse.sw360.datahandler.thrift.packages.Package; -import org.eclipse.sw360.datahandler.thrift.projects.Project; -import org.eclipse.sw360.datahandler.thrift.users.User; -import org.ektorp.http.HttpClient; -import org.ektorp.http.URI; -import org.ektorp.support.DesignDocument; -import java.io.IOException; -import java.util.*; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.cloudant.client.api.CloudantClient; -import com.fasterxml.jackson.databind.ObjectMapper; - -import static com.google.common.base.Strings.isNullOrEmpty; -import static com.google.common.base.Strings.nullToEmpty; - - -/** - * Generic database connector for handling lucene searches - * - * @author cedric.bodet@tngtech.com - * @author alex.borodin@evosoft.com - */ -public class LuceneAwareDatabaseConnector extends LuceneAwareCouchDbConnector { - - private static final Logger log = LogManager.getLogger(LuceneAwareDatabaseConnector.class); - - private static final Joiner AND = Joiner.on(" AND "); - private static final Joiner OR = Joiner.on(" OR "); - - private final DatabaseConnectorCloudant connector; - - private static final List LUCENE_SPECIAL_CHARACTERS = Arrays.asList("[\\\\\\+\\-\\!\\~\\*\\?\\\"\\^\\:\\(\\)\\{\\}\\[\\]]", "\\&\\&", "\\|\\|", "/"); - - private String dbNameForLuceneSearch; - /** - * Maximum number of results to return - */ - private int resultLimit = 0; - - /** - * URL/DbName constructor - */ - - public LuceneAwareDatabaseConnector(Supplier httpClient, Supplier cClient, String dbName) throws IOException { - this(new DatabaseConnector(httpClient, dbName), cClient); - } - - /** - * Constructor using a Database connector - */ - public LuceneAwareDatabaseConnector(DatabaseConnector connector, Supplier cClient) throws IOException { - super(connector.getDbName(), connector.getInstance()); - this.dbNameForLuceneSearch = connector.getDbName(); - setResultLimit(DatabaseSettings.LUCENE_SEARCH_LIMIT); - this.connector = new DatabaseConnectorCloudant(cClient, connector.getDbName()); - } - - public boolean addView(LuceneSearchView function) { - // make sure that the indexer is up-to-date - IndexUploader uploader = new IndexUploader(); - return uploader.updateSearchFunctionIfNecessary(this, function.searchView, - function.searchFunction, function.searchBody); - } - - /** - * Search with lucene using the previously declared search function - */ - public List searchView(Class type, LuceneSearchView function, String queryString) { - return connector.get(type, searchIds(type,function, queryString)); - } - - /** - * Search with lucene using the previously declared search function only for ids - */ - public List searchIds(Class type, LuceneSearchView function, String queryString) { - LuceneResult queryLuceneResult = searchView(function, queryString, false); - return getIdsFromResult(queryLuceneResult); - } - - /** - * Search, sort and translate Lucene Result - */ - public List searchAndSortByScore(Class type, LuceneSearchView function, String queryString) { - LuceneResult queryLuceneResult = searchView(function, queryString); - Collections.sort(queryLuceneResult.getRows(), new LuceneResultComparator()); - List results = new ArrayList(); - Gson gson = new Gson(); - for (LuceneResult.Row row : queryLuceneResult.getRows()) { - if(row != null && row.getDoc() != null && !row.getDoc().isEmpty() && !row.getDoc().containsKey("error") ) { - results.add(gson.fromJson(gson.toJson(row.getDoc()), type)); - } - } - return results; - } - /** - * Comparator to provide ordered search results - */ - public class LuceneResultComparator implements Comparator { - @Override - public int compare(LuceneResult.Row o1, LuceneResult.Row o2) { - return -Double.compare(o1.getScore(), o2.getScore()); - } - } - - /** - * Search with lucene using the previously declared search function - */ - public LuceneResult searchView(LuceneSearchView function, String queryString) { - return searchView(function, queryString, true); - } - - /** - * Search with lucene using the previously declared search function - */ - private LuceneResult searchView(LuceneSearchView function, String queryString, boolean includeDocs) { - if (isNullOrEmpty(queryString)) { - return null; - } - - try { - LuceneResult callLuceneDirectly = callLuceneDirectly(function, queryString, includeDocs); - return callLuceneDirectly; - } catch (Exception exp) { - log.error("Error querying Lucene directly.", exp); - } - return null; - } - - private LuceneResult callLuceneDirectly(LuceneSearchView function, String queryString, boolean includeDocs) - throws IOException { - URI queryURI = URI.of("/"); - queryURI.append(DEFAULT_LUCENE_INDEX); - queryURI.append(dbNameForLuceneSearch); - queryURI.append(function.searchView.startsWith(DesignDocument.ID_PREFIX) ? function.searchView - : DesignDocument.ID_PREFIX + function.searchView); - queryURI.append(function.searchFunction); - queryURI.param("include_docs", Boolean.valueOf(includeDocs).toString()); - if (resultLimit > 0) { - queryURI.param("limit", resultLimit); - } - queryURI.param("q", queryString.toString()); - URL luceneResourceUrl = new URL(DatabaseSettings.COUCH_DB_LUCENE_URL + queryURI.toString()); - ObjectMapper objectMapper = new EktorpLuceneObjectMapperFactory().createObjectMapper(); - HttpURLConnection connection = null; - try { - connection = makeLuceneRequest(luceneResourceUrl); - log.info("Lucene search URL = " + luceneResourceUrl.toString()); - int responseCode = connection.getResponseCode(); - if (responseCode == 200) { - return objectMapper.readValue(connection.getInputStream(), LuceneResult.class); - } else { - connection.disconnect(); - log.error("Getting error with response code = " + responseCode + ". Retrying with stale parameter"); - queryURI.param("stale", "ok"); - luceneResourceUrl = new URL(DatabaseSettings.COUCH_DB_LUCENE_URL + queryURI.toString()); - connection = makeLuceneRequest(luceneResourceUrl); - log.info("Stale Parameter Lucene search URL = " + luceneResourceUrl.toString()); - responseCode = connection.getResponseCode(); - if (responseCode == 200) { - return objectMapper.readValue(connection.getInputStream(), LuceneResult.class); - } else { - log.error("Retried with stale parameter.Getting error with reponse code=" + responseCode); - } - } - } finally { - if (connection != null) { - connection.disconnect(); - } - } - return null; - } - - private HttpURLConnection makeLuceneRequest(URL luceneResourceUrl) throws IOException { - HttpURLConnection connection = (HttpURLConnection) luceneResourceUrl.openConnection(); - connection.setRequestMethod("GET"); - connection.connect(); - return connection; - } - - ///////////////////////// - // GETTERS AND SETTERS // - ///////////////////////// - - private void setQueryLimit(LuceneQuery query) { - if (resultLimit > 0) { - query.setLimit(resultLimit); - } - } - - public void setResultLimit(int limit) { - if (limit >= 0) { - resultLimit = limit; - } - } - - //////////////////// - // HELPER METHODS // - //////////////////// - - private static List getIdsFromResult(LuceneResult result) { - List ids = new ArrayList<>(); - if (result != null) { - for (LuceneResult.Row row : result.getRows()) { - ids.add(row.getId()); - } - } - return ids; - } - - - /** - * Search the database for a given string and types - */ - public List searchViewWithRestrictions(Class type, LuceneSearchView luceneSearchView, String text, final Map> subQueryRestrictions) { - List subQueries = new ArrayList<>(); - for (Map.Entry> restriction : subQueryRestrictions.entrySet()) { - - final Set filterSet = restriction.getValue(); - - if (!filterSet.isEmpty()) { - final String fieldName = restriction.getKey(); - String subQuery = formatSubquery(filterSet, fieldName); - subQueries.add(subQuery); - } - } - - if (!isNullOrEmpty(text)) { - subQueries.add(prepareWildcardQuery(text)); - } - - if (type == Package.class && subQueryRestrictions.keySet().contains("orphanPackageCheckBox")) { - // get all packages with name field and then negate with releaseId field to find orphan packages - subQueries.add("(name:*) NOT (releaseId:*)"); - } - String query = AND.join(subQueries); - - return searchView(type, luceneSearchView, query); - } - - public List searchProjectViewWithRestrictionsAndFilter(LuceneSearchView luceneSearchView, String text, - final Map> subQueryRestrictions, User user) { - List projectList = searchViewWithRestrictions(Project.class, luceneSearchView, text, - subQueryRestrictions); - return projectList.stream().filter(ProjectPermissions.isVisible(user)).collect(Collectors.toList()); - } - - private static String formatSubquery(Set filterSet, final String fieldName) { - final Function addType = input -> { - if (fieldName.equals("businessUnit") || fieldName.equals("tag") || fieldName.equals("projectResponsible") || fieldName.equals("createdBy")) { - return fieldName + ":\"" + input + "\""; - } if (fieldName.equals("createdOn") || fieldName.equals("timestamp")) { - return fieldName + ":" + input; - } else { - return fieldName + ":" + input; - } - }; - - Stream searchFilters = filterSet.stream().map(addType); - return "( " + OR.join(searchFilters.collect(Collectors.toList())) + " ) "; - } - - public static String prepareWildcardQuery(String query) { - String leadingWildcardChar = DatabaseSettings.LUCENE_LEADING_WILDCARD ? "*" : ""; - if (query.startsWith("\"") && query.endsWith("\"")) { - return "(\"" + sanitizeQueryInput(query) + "\")"; - } else { - String wildCardQuery = Arrays.stream(sanitizeQueryInput(query) - .split(" ")).map(q -> leadingWildcardChar + q + "*") - .collect(Collectors.joining(" ")); - return "(\"" + wildCardQuery + "\" " + wildCardQuery + ")"; - } - } - - public static String prepareFuzzyQuery(String query) { - return sanitizeQueryInput(query) + "~"; - } - - private static String sanitizeQueryInput(String input) { - if (isNullOrEmpty(input)) { - return nullToEmpty(input); - } else { - for (String removeStr : LUCENE_SPECIAL_CHARACTERS) { - input = input.replaceAll(removeStr, " "); - } - return input.replaceAll("\\s+", " ").trim(); - } - } -} diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java new file mode 100644 index 0000000000..25184966c4 --- /dev/null +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java @@ -0,0 +1,340 @@ +/* + * Copyright Siemens AG, 2024. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.couchdb.lucene; + +import com.cloudant.client.api.CloudantClient; +import com.cloudant.client.api.DesignDocumentManager; +import com.cloudant.client.api.model.Response; +import com.cloudant.client.org.lightcouch.NoDocumentException; +import com.cloudant.client.org.lightcouch.internal.CouchDbUtil; +import com.google.common.base.Joiner; +import com.google.gson.Gson; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.common.DatabaseSettings; +import org.eclipse.sw360.datahandler.permissions.ProjectPermissions; +import org.eclipse.sw360.datahandler.thrift.packages.Package; +import org.eclipse.sw360.datahandler.thrift.projects.Project; +import org.eclipse.sw360.datahandler.thrift.users.User; +import org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector; +import org.eclipse.sw360.nouveau.NouveauQuery; +import org.eclipse.sw360.nouveau.NouveauResult; +import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static com.google.common.base.Strings.nullToEmpty; + + +/** + * Generic database connector for handling lucene searches + * + * @author cedric.bodet@tngtech.com + * @author alex.borodin@evosoft.com + */ +public class NouveauLuceneAwareDatabaseConnector extends LuceneAwareCouchDbConnector { + + private static final Logger log = LogManager.getLogger(NouveauLuceneAwareDatabaseConnector.class); + + private static final Joiner AND = Joiner.on(" AND "); + private static final Joiner OR = Joiner.on(" OR "); + private static final String RANGE_TO = " TO "; + + private final DatabaseConnectorCloudant connector; + + private static final List LUCENE_SPECIAL_CHARACTERS = Arrays.asList("[\\\\\\+\\-\\!\\~\\*\\?\\\"\\^\\:\\(\\)\\{\\}\\[\\]]", "\\&\\&", "\\|\\|", "/"); + + /** + * Maximum number of results to return + */ + private int resultLimit = 0; + + /** + * Constructor using a Database connector + */ + public NouveauLuceneAwareDatabaseConnector(@NotNull DatabaseConnectorCloudant db, + Supplier dbClient, + String ddoc) throws IOException { + super(db.getDatabase(), dbClient, ddoc); + setResultLimit(DatabaseSettings.LUCENE_SEARCH_LIMIT); + this.connector = db; + } + + public boolean addDesignDoc(@NotNull NouveauDesignDocument designDocument) { + DesignDocumentManager designDocumentManager = this.connector.getDesignDocumentManager(); + + NouveauDesignDocument documentFromDb; + try { + documentFromDb = this.getNouveauDesignDocument(designDocument.getId()); + } catch (NoDocumentException ex) { + return putNouveauDesignDocument(designDocument, designDocumentManager); + } + + if (!designDocument.equals(documentFromDb)) { + designDocument.setRevision(documentFromDb.getRevision()); + if (documentFromDb.getNouveau() != null) { + // Add missing indexes from existing DDOC as to not overwrite them + documentFromDb.getNouveau().asMap().forEach((key, value) -> { + if (! designDocument.getNouveau().has(key)) { + designDocument.getNouveau().add(key, value); + } + }); + } + return putNouveauDesignDocument(designDocument, designDocumentManager); + } + return true; + } + + /** + * Search with lucene using the previously declared search function + */ + public List searchView(Class type, String indexName, String queryString) { + return connector.get(type, searchIds(type, indexName, queryString)); + } + + /** + * Search with lucene using the previously declared search function only for ids + */ + public List searchIds(Class type, String indexName, String queryString) { + NouveauResult queryNouveauResult = searchView(indexName, queryString, false); + return getIdsFromResult(queryNouveauResult); + } + + /** + * Search, sort and translate Lucene Result + */ + public List searchAndSortByScore(Class type, String indexName, String queryString) { + NouveauResult queryNouveauResult = searchView(indexName, queryString); + List hits = queryNouveauResult.getHits(); + hits.sort(new NouveauResultComparator()); + List results = new ArrayList<>(); + Gson gson = new Gson(); + for (NouveauResult.Hits hit : hits) { + if (hit != null && hit.getDoc() != null && !hit.getDoc().isEmpty()) { + results.add(gson.fromJson(gson.toJson(hit.getDoc()), type)); + } + } + return results; + } + + /** + * Comparator to provide ordered search results + */ + public class NouveauResultComparator implements Comparator { + @Override + public int compare(NouveauResult.Hits o1, NouveauResult.Hits o2) { + double order1 = 0.0; + double order2 = 0.0; + for (LinkedHashMap order : o1.getOrder()) { + if (order.get("@type").equals("float")) { + order1 = Double.parseDouble(String.valueOf(order.get("value"))); + break; + } + } + for (LinkedHashMap order : o2.getOrder()) { + if (order.get("@type").equals("float")) { + order2 = Double.parseDouble(String.valueOf(order.get("value"))); + break; + } + } + return Double.compare(order1, order2); + } + } + + /** + * Search with lucene using the previously declared search function + */ + public NouveauResult searchView(String indexName, String queryString) { + return searchView(indexName, queryString, true); + } + + /** + * Search with lucene using the previously declared search function + */ + private @Nullable NouveauResult searchView(String indexName, String queryString, boolean includeDocs) { + if (isNullOrEmpty(queryString)) { + return null; + } + + return callLuceneDirectly(indexName, queryString, includeDocs); + } + + private NouveauResult callLuceneDirectly(String indexName, String queryString, boolean includeDocs) { + NouveauQuery query = new NouveauQuery(queryString); + query.setIncludeDocs(includeDocs); + if (resultLimit > 0) { + query.setLimit(resultLimit); + } + return queryNouveau(indexName, query); + } + + ///////////////////////// + // GETTERS AND SETTERS // + ///////////////////////// + + public void setResultLimit(int limit) { + if (limit >= 0) { + resultLimit = limit; + } + } + + //////////////////// + // HELPER METHODS // + //////////////////// + private static @NotNull List getIdsFromResult(NouveauResult result) { + List ids = new ArrayList<>(); + if (result != null) { + for (NouveauResult.Hits hit : result.getHits()) { + ids.add(hit.getId()); + } + } + return ids; + } + + /** + * Search the database for a given string and types + */ + public List searchViewWithRestrictions(Class type, String indexName, + String text, + final @NotNull Map> subQueryRestrictions) { + List subQueries = new ArrayList<>(); + for (Map.Entry> restriction : subQueryRestrictions.entrySet()) { + + final Set filterSet = restriction.getValue(); + + if (!filterSet.isEmpty()) { + final String fieldName = restriction.getKey(); + String subQuery = formatSubquery(filterSet, fieldName); + subQueries.add(subQuery); + } + } + + if (!isNullOrEmpty(text)) { + subQueries.add(prepareWildcardQuery(text)); + } + + if (type == Package.class && subQueryRestrictions.containsKey("orphanPackageCheckBox")) { + // get all packages with name field and then negate with releaseId field to find orphan packages + subQueries.add("(name:*) NOT (releaseId:*)"); + } + String query = AND.join(subQueries); + + return searchView(type, indexName, query); + } + + private static @NotNull String formatSubquery(@NotNull Set filterSet, final String fieldName) { + final Function addType = input -> { + if (fieldName.equals("businessUnit") || fieldName.equals("tag") || fieldName.equals("projectResponsible") || fieldName.equals("createdBy")) { + return fieldName + ":\"" + input + "\""; + } if (fieldName.equals("createdOn") || fieldName.equals("timestamp")) { + try { + return fieldName + ":" + formatDateNouveauFormat(input); + } catch (ParseException e) { + return fieldName + ":" + input; + } + } else { + return fieldName + ":" + input; + } + }; + + Stream searchFilters = filterSet.stream().map(addType); + return "( " + OR.join(searchFilters.collect(Collectors.toList())) + " ) "; + } + + public static @NotNull String prepareWildcardQuery(@NotNull String query) { + String leadingWildcardChar = DatabaseSettings.LUCENE_LEADING_WILDCARD ? "*" : ""; + if (query.startsWith("\"") && query.endsWith("\"")) { + return "(\"" + sanitizeQueryInput(query) + "\")"; + } else { + String wildCardQuery = Arrays.stream(sanitizeQueryInput(query) + .split(" ")).map(q -> leadingWildcardChar + q + "*") + .collect(Collectors.joining(" ")); + return "(\"" + wildCardQuery + "\" " + wildCardQuery + ")"; + } + } + + public static @NotNull String prepareFuzzyQuery(String query) { + return sanitizeQueryInput(query) + "~"; + } + + public List searchProjectViewWithRestrictionsAndFilter(String indexName, String text, + final Map> subQueryRestrictions, + User user) { + List projectList = searchViewWithRestrictions(Project.class, indexName, text, + subQueryRestrictions); + return projectList.stream().filter(ProjectPermissions.isVisible(user)).collect(Collectors.toList()); + } + + private static String sanitizeQueryInput(String input) { + if (isNullOrEmpty(input)) { + return nullToEmpty(input); + } else { + for (String removeStr : LUCENE_SPECIAL_CHARACTERS) { + input = input.replaceAll(removeStr, " "); + } + return input.replaceAll("\\s+", " ").trim(); + } + } + + private NouveauDesignDocument getNouveauDesignDocument(String id) { + CouchDbUtil.assertNotEmpty(id, "id"); + return this.connector.getDatabase().find(NouveauDesignDocument.class, id); + } + + private static boolean putNouveauDesignDocument(NouveauDesignDocument designDocument, + @NotNull DesignDocumentManager designDocumentManager) { + Response response = designDocumentManager.put(designDocument); + return response.getError() == null || response.getError().isEmpty(); + } + + private static @NotNull String formatDateNouveauFormat(@NotNull String date) throws ParseException { + if (date.startsWith("[") && date.toUpperCase().contains(RANGE_TO)) { + return formatDateRangesNouveauFormat(date); + } + return dateToNouveauDouble(date); + } + + private static @NotNull String formatDateRangesNouveauFormat(@NotNull String date) throws ParseException { + String[] dates = date.toUpperCase().substring(1, date.length() - 1).split(RANGE_TO); + return "[" + dateToNouveauDouble(dates[0]) + RANGE_TO + dateToNouveauDouble(dates[1]) + "]"; + } + + public static @NotNull String dateToNouveauDouble(String date) throws ParseException { + SimpleDateFormat outputFormatter = new SimpleDateFormat("yyyyMMdd"); + SimpleDateFormat inputFormatterDate = new SimpleDateFormat("yyyy-MM-dd"); + Date parsedDate; + try { + parsedDate = inputFormatterDate.parse(date); + } catch (ParseException e) { + parsedDate = new Date(Long.parseLong(date)); + } catch (Exception e) { + throw new ParseException("Date format not recognized", 0); + } + return outputFormatter.format(parsedDate.getTime()); + } +} diff --git a/libraries/nouveau-handler/pom.xml b/libraries/nouveau-handler/pom.xml new file mode 100644 index 0000000000..8b543fd347 --- /dev/null +++ b/libraries/nouveau-handler/pom.xml @@ -0,0 +1,137 @@ + + + + 4.0.0 + + + libraries + org.eclipse.sw360 + ${revision} + + + nouveau-handler + nouveau-handler + jar + + + ${liferay.deploy.dir} + + + + org.eclipse.sw360.nouveau-handler-${project.version} + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + + biz.aQute.bnd + bnd-maven-plugin + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + + test-jar + + + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven-dependency-plugin.version} + + + add-build-configuration-resources + + unpack + + generate-resources + + + + + org.eclipse.sw360 + build-configuration + ${project.version} + + jar + ${project.build.outputDirectory} + + + + + + add-build-configuration-test-resources + + unpack + + generate-test-resources + + + + + org.eclipse.sw360 + build-configuration + ${project.version} + + test-jar + ${project.build.testOutputDirectory} + + + + + + + + + + + com.cloudant + cloudant-client + ${cloudant.version} + + + commons-lang + commons-lang + 2.4 + + + junit + junit + ${junit.version} + test + + + joda-time + joda-time + 1.6.2 + + + org.jetbrains + annotations + 15.0 + + + + diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CommonHits.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CommonHits.java new file mode 100644 index 0000000000..45da7b7fe1 --- /dev/null +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CommonHits.java @@ -0,0 +1,51 @@ +/* + * Copyright Siemens AG, 2024. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.eclipse.sw360.nouveau; + +import java.util.LinkedHashMap; +import java.util.List; + +/** + * Represents the Hits of a Lucene query. + */ +class CommonHits { + protected List> order; + protected String id; + protected LinkedHashMap fields; + + public List> getOrder() { + return order; + } + + public String getId() { + return id; + } + + public LinkedHashMap getFields() { + return fields; + } + + /** + * Returns the score of the hit which is typically the only float value in + * the order list. + * @return Score of the hit (higher is better) + */ + public double getScore() { + double score = 0.0; + for (LinkedHashMap order : this.getOrder()) { + if (order.get("@type").equals("float")) { + score = Double.parseDouble(String.valueOf(order.get("value"))); + break; + } + } + return score; + } +} diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CommonNouveauResult.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CommonNouveauResult.java new file mode 100644 index 0000000000..3e32d66dc4 --- /dev/null +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CommonNouveauResult.java @@ -0,0 +1,54 @@ +/* + * Copyright Siemens AG, 2024. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.eclipse.sw360.nouveau; + +import com.google.gson.annotations.SerializedName; + +import java.io.Serializable; +import java.util.Map; + +/** + * The result of the lucene query. + */ +class CommonNouveauResult implements Serializable { + + @SerializedName("total_hits_relation") + protected String totalHitsRelation; + @SerializedName("total_hits") + protected long totalHits; + @SerializedName("ranges") + protected Map> ranges; + @SerializedName("counts") + protected Map> counts; + @SerializedName("bookmark") + protected String bookmark; + + public String getTotalHitsRelation() { + return totalHitsRelation; + } + + public long getTotalHits() { + return totalHits; + } + + public Map> getRanges() { + return ranges; + } + + public Map> getCounts() { + return counts; + } + + public String getBookmark() { + return bookmark; + } +} + diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CustomNouveauResult.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CustomNouveauResult.java new file mode 100644 index 0000000000..03b327e52b --- /dev/null +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CustomNouveauResult.java @@ -0,0 +1,34 @@ +/* + * Copyright Siemens AG, 2024. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.eclipse.sw360.nouveau; + +import java.util.List; + +/** + * The result of the lucene query with hits containing the document. + * @param Type of the document expected to be contained in the hit. + */ +public class CustomNouveauResult extends CommonNouveauResult { + + private List> hits; + + public void setHits(List> hits) { + this.hits = hits; + } + + public List> getHits() { + return hits; + } + + public static class Hits extends CommonHits { + private T doc; + } +} diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java new file mode 100644 index 0000000000..1a13032740 --- /dev/null +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java @@ -0,0 +1,101 @@ +/* + * Copyright Siemens AG, 2024. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.nouveau; + +import com.cloudant.client.api.CloudantClient; +import com.cloudant.client.api.Database; +import com.cloudant.client.internal.DatabaseURIHelper; +import com.cloudant.client.org.lightcouch.CouchDbException; +import com.cloudant.client.org.lightcouch.internal.CouchDbUtil; +import com.cloudant.http.HttpConnection; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.util.function.Supplier; + +/** + * CouchDB connector which allows us to run the Nouveau queries. + */ +public class LuceneAwareCouchDbConnector { + + public static String DEFAULT_NOUVEAU_PREFIX = "_nouveau"; + public static String DEFAULT_DESIGN_PREFIX = "_design"; + + private final String lucenePrefix; + private final String ddoc; + private final NouveauAwareDatabase database; + + public LuceneAwareCouchDbConnector(Database db, Supplier dbClient, String ddoc) throws IOException { + this.lucenePrefix = DEFAULT_NOUVEAU_PREFIX; + this.ddoc = ddoc; + this.database = new NouveauAwareDatabase(db, dbClient, this.ddoc, this.lucenePrefix); + } + + public static class NouveauAwareDatabase extends Database { + private final String ddoc; + private final CloudantClient client; + private final String lucenePrefix; + + protected NouveauAwareDatabase(Database db, @NotNull Supplier dbClient, + String ddoc, String lucenePrefix) { + super(db); + this.client = dbClient.get(); + this.ddoc = ddoc; + this.lucenePrefix = lucenePrefix; + } + + public T queryNouveau(String index, @NotNull NouveauQuery query, Class classOfT) { + URI uri = (new DatabaseURIHelper(this.getDBUri())) + .path(ensureDesignId(this.ddoc)) + .path(lucenePrefix).path(index).build(); + + InputStream response = null; + + T queryResponse; + try { + response = this.client.executeRequest(CouchDbUtil.createPost(uri, query.buildQuery(this.client.getGson()), "application/json")).responseAsInputStream(); + queryResponse = CouchDbUtil.getResponse(response, classOfT, this.client.getGson()); + } catch (IOException | CouchDbException e) { + throw new RuntimeException(e); + } finally { + CouchDbUtil.close(response); + } + + return queryResponse; + } + } + + /** + * Query the Nouveau index. + * @param index The name of the index. + * @param query The query to run. + * @return The result of the query. + */ + public NouveauResult queryNouveau(String index, @NotNull NouveauQuery query) { + return this.database.queryNouveau(index, query, NouveauResult.class); + } + + /** + * Ensure that the design ID is prefixed with the default design prefix. + * @param designId The design ID to validate. + * @return The design ID with the default design prefix. + */ + @Contract(pure = true) + public static @NotNull String ensureDesignId(@NotNull String designId) { + if (designId.startsWith(DEFAULT_DESIGN_PREFIX)) { + return designId; + } else { + return DEFAULT_DESIGN_PREFIX + "/" + designId; + } + } +} diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/NouveauQuery.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/NouveauQuery.java new file mode 100644 index 0000000000..55772ca2b6 --- /dev/null +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/NouveauQuery.java @@ -0,0 +1,164 @@ +/* + * Copyright Siemens AG, 2024. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.nouveau; + +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; + +import java.util.List; +import java.util.Objects; + +/** + * Nouveau query, documented from endpoint + * /{db}/_design/{ddoc}/_nouveau/{index} + */ +public class NouveauQuery { + + private String bookmark; + private List counts; + @SerializedName("include_docs") + private Boolean includeDocs; + private String locale; + private Integer limit; + @SerializedName("q") + private String query; + private List ranges; + private String sort; + private Boolean update; + + public static class Range { + private String label; + private Integer min; + private Integer max; + @SerializedName("min_inclusive") + private Boolean minInclusive; + @SerializedName("max_inclusive") + private Boolean maxInclusive; + } + + private String cachedQuery; + + public NouveauQuery(String query) { + this.query = query; + } + + /** + * Set the bookmark from previous search to enable pagination. + * @param bookmark Bookmark from previous search + */ + public void setBookmark(String bookmark) { + this.bookmark = bookmark; + } + + /** + * Array of names of string fields for which counts are requested. + * @param counts Array of string fields + */ + public void setCounts(List counts) { + this.counts = counts; + } + + /** + * Include the full content of the documents in the result. + * @param includeDocs Get full content of documents + */ + public void setIncludeDocs(Boolean includeDocs) { + this.includeDocs = includeDocs; + } + + /** + * The java locale to parse numbers in range. Example: "de", "us", "gb". + * @param locale Locale to parse numbers + */ + public void setLocale(String locale) { + this.locale = locale; + } + + /** + * Limit the number of returned documents. + * @param limit Limit of returned documents + */ + public void setLimit(Integer limit) { + this.limit = limit; + } + + /** + * The Lucene query string. + * @param query Lucene query string + */ + public void setQuery(String query) { + this.query = query; + } + + /** + * Define ranges for numeric search fields. Example: {"bar":[{"label":"cheap","min":0,"max":100}]} + * @param ranges List of ranges + */ + public void setRanges(List ranges) { + this.ranges = ranges; + } + + /** + * Sort the results by given sort order. Example: "fieldname" or "-fieldname". + * @param sort Sort order + */ + public void setSort(String sort) { + this.sort = sort; + } + + /** + * Set as false to allow the use of an out-of-date index. + * @param update Allow out-of-date index + */ + public void setUpdated(Boolean update) { + this.update = update; + } + + /** + * Build the query from the current state of the object. + * @param gson Gson object + * @return JSON string of the query to be used as POST body. + */ + String buildQuery(final Gson gson) { + if (cachedQuery != null) { + return cachedQuery; + } + + cachedQuery = gson.toJson(this); + return cachedQuery; + } + + public void reset() { + this.cachedQuery = null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NouveauQuery that = (NouveauQuery) o; + return Objects.equals(bookmark, that.bookmark) && + Objects.equals(counts, that.counts) && + Objects.equals(includeDocs, that.includeDocs) && + Objects.equals(locale, that.locale) && + Objects.equals(limit, that.limit) && + Objects.equals(query, that.query) && + Objects.equals(ranges, that.ranges) && + Objects.equals(sort, that.sort) && + Objects.equals(update, that.update) && + Objects.equals(cachedQuery, that.cachedQuery); + } + + @Override + public int hashCode() { + return Objects.hash(bookmark, counts, includeDocs, locale, limit, query, + ranges, sort, update, cachedQuery); + } +} diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/NouveauResult.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/NouveauResult.java new file mode 100644 index 0000000000..06a254c691 --- /dev/null +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/NouveauResult.java @@ -0,0 +1,45 @@ +/* + * Copyright Siemens AG, 2024. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.nouveau; + +import java.util.LinkedHashMap; +import java.util.List; + +/** + * The result of the lucene query. + */ +public class NouveauResult extends CommonNouveauResult { + + private List hits; + + public List getHits() { + return hits; + } + + public void setHits(List hits) { + this.hits = hits; + } + + public static class Hits extends CommonHits { + + private LinkedHashMap doc; + + /** + * The stored contents of the document (when include_docs=true) + */ + public LinkedHashMap getDoc() { + return doc; + } + + public void setDoc(LinkedHashMap doc) { + this.doc = doc; + } + } +} diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauDesignDocument.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauDesignDocument.java new file mode 100644 index 0000000000..86f9422fbc --- /dev/null +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauDesignDocument.java @@ -0,0 +1,60 @@ +/* + * Copyright Siemens AG, 2024. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.nouveau.designdocument; + +import com.cloudant.client.api.model.DesignDocument; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * CouchDB Design document that contains nouveau indexes. + */ +public class NouveauDesignDocument extends DesignDocument { + + private JsonObject nouveau; + + public JsonObject getNouveau() { + return this.nouveau; + } + + public void setNouveau(JsonObject nouveau) { + this.nouveau = nouveau; + } + + public void addNouveau(NouveauIndexDesignDocument indexDesign, + @NotNull Gson gson) { + if (this.nouveau == null) { + this.nouveau = new JsonObject(); + } + this.nouveau.add(indexDesign.getIndexName(), gson.toJsonTree(indexDesign.getIndexFunction())); + } + + public int hashCode() { + return 31 * super.hashCode() + (this.nouveau != null ? this.nouveau.hashCode() : 0); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + if (super.equals(o)) { + return true; + } else { + NouveauDesignDocument that = (NouveauDesignDocument) o; + return Objects.equals(this.nouveau, that.nouveau); + } + } else { + return false; + } + } +} diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauIndexDesignDocument.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauIndexDesignDocument.java new file mode 100644 index 0000000000..efd75a7ea5 --- /dev/null +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauIndexDesignDocument.java @@ -0,0 +1,48 @@ +/* + * Copyright Siemens AG, 2024. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.eclipse.sw360.nouveau.designdocument; + +import java.util.Objects; + +/** + * Represents a design document for a lucene index. Contains the index name + * and the index function. + */ +public class NouveauIndexDesignDocument { + private final String indexName; + private final NouveauIndexFunction indexFunction; + + public NouveauIndexDesignDocument(String indexName, NouveauIndexFunction indexFunction) { + this.indexName = indexName; + this.indexFunction = indexFunction; + } + + public String getIndexName() { + return indexName; + } + + public NouveauIndexFunction getIndexFunction() { + return indexFunction; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NouveauIndexDesignDocument that = (NouveauIndexDesignDocument) o; + return Objects.equals(indexName, that.indexName) && Objects.equals(indexFunction, that.indexFunction); + } + + @Override + public int hashCode() { + return Objects.hash(indexName, indexFunction); + } +} diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauIndexFunction.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauIndexFunction.java new file mode 100644 index 0000000000..7a1c3a9982 --- /dev/null +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauIndexFunction.java @@ -0,0 +1,71 @@ +/* + * Copyright Siemens AG, 2024. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.eclipse.sw360.nouveau.designdocument; + +import com.google.gson.annotations.SerializedName; + +import java.util.Map; +import java.util.Objects; + +/** + * Represents the index function for a nouveau index. Provide `default_analyzer` + * and `field_analyzers` to specify the analyzers for the fields. + */ +public class NouveauIndexFunction { + @SerializedName("default_analyzer") + private String defaultAnalyzer; + @SerializedName("field_analyzers") + private Map fieldAnalyzer; + @SerializedName("index") + private final String index; + + public NouveauIndexFunction(String index) { + if (index == null || index.isEmpty()) { + throw new IllegalArgumentException("Index cannot cannot be empty!"); + } + this.index = index; + } + + public String getDefaultAnalyzer() { + return defaultAnalyzer; + } + + public NouveauIndexFunction setDefaultAnalyzer(String defaultAnalyzer) { + this.defaultAnalyzer = defaultAnalyzer; + return this; + } + + public Map getFieldAnalyzer() { + return fieldAnalyzer; + } + + public NouveauIndexFunction setFieldAnalyzer(Map fieldAnalyzer) { + this.fieldAnalyzer = fieldAnalyzer; + return this; + } + + public String getIndex() { + return index; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NouveauIndexFunction that = (NouveauIndexFunction) o; + return Objects.equals(defaultAnalyzer, that.defaultAnalyzer) && Objects.equals(fieldAnalyzer, that.fieldAnalyzer) && Objects.equals(index, that.index); + } + + @Override + public int hashCode() { + return Objects.hash(defaultAnalyzer, fieldAnalyzer, index); + } +} diff --git a/libraries/nouveau-handler/src/test/java/org/eclipse/sw360/nouveau/CommonHitsTest.java b/libraries/nouveau-handler/src/test/java/org/eclipse/sw360/nouveau/CommonHitsTest.java new file mode 100644 index 0000000000..f2f18073dd --- /dev/null +++ b/libraries/nouveau-handler/src/test/java/org/eclipse/sw360/nouveau/CommonHitsTest.java @@ -0,0 +1,40 @@ +/* + * Copyright Siemens AG, 2024. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.eclipse.sw360.nouveau; + +import junit.framework.TestCase; + +import java.util.LinkedHashMap; +import java.util.List; + +public class CommonHitsTest extends TestCase { + + public void testGetScoreSet() { + CommonHits commonHits = new CommonHits(); + LinkedHashMap order = new LinkedHashMap<>(); + order.put("@type", "float"); + order.put("value", 1); + LinkedHashMap id = new LinkedHashMap<>(); + id.put("@type", "string"); + id.put("value", "deadbeef"); + commonHits.order = List.of(id, order); + assert commonHits.getScore() == 1.0; + } + + public void testGetScoreUnset() { + CommonHits commonHits = new CommonHits(); + LinkedHashMap id = new LinkedHashMap<>(); + id.put("@type", "string"); + id.put("value", "deadbeef"); + commonHits.order = List.of(id); + assert commonHits.getScore() == 0.0; + } +} diff --git a/libraries/nouveau-handler/src/test/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnectorTest.java b/libraries/nouveau-handler/src/test/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnectorTest.java new file mode 100644 index 0000000000..2927d824cd --- /dev/null +++ b/libraries/nouveau-handler/src/test/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnectorTest.java @@ -0,0 +1,24 @@ +/* + * Copyright Siemens AG, 2024. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.eclipse.sw360.nouveau; + +import junit.framework.TestCase; + +public class LuceneAwareCouchDbConnectorTest extends TestCase { + + public void testEnsureDesignIdMissing() { + assert "_design/lucene".equals(LuceneAwareCouchDbConnector.ensureDesignId("lucene")); + } + + public void testEnsureDesignIdContaining() { + assert "_design/lucene".equals(LuceneAwareCouchDbConnector.ensureDesignId("_design/lucene")); + } +} diff --git a/libraries/pom.xml b/libraries/pom.xml index 9ce0381311..40d972c6b1 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -28,6 +28,7 @@ commonIO importers exporters + nouveau-handler diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/component/ComponentController.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/component/ComponentController.java index f9e6866e6a..247d3a7b5a 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/component/ComponentController.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/component/ComponentController.java @@ -23,7 +23,7 @@ import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.common.SW360Constants; import org.eclipse.sw360.datahandler.common.SW360Utils; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.resourcelists.PaginationParameterException; import org.eclipse.sw360.datahandler.resourcelists.PaginationResult; import org.eclipse.sw360.datahandler.resourcelists.ResourceClassNotFoundException; @@ -31,7 +31,6 @@ import org.eclipse.sw360.datahandler.thrift.RequestSummary; import org.eclipse.sw360.datahandler.thrift.ImportBomRequestPreparation; import org.eclipse.sw360.datahandler.thrift.RestrictedResource; -import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.thrift.Source; import org.eclipse.sw360.datahandler.thrift.VerificationStateInfo; import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; @@ -167,7 +166,7 @@ public ResponseEntity>> getComponents( } if (CommonUtils.isNotNullEmptyOrWhitespace(name)) { Set values = CommonUtils.splitToSet(name); - values = values.stream().map(LuceneAwareDatabaseConnector::prepareWildcardQuery) + values = values.stream().map(NouveauLuceneAwareDatabaseConnector::prepareWildcardQuery) .collect(Collectors.toSet()); filterMap.put(Component._Fields.NAME.getFieldName(), values); } diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/packages/SW360PackageService.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/packages/SW360PackageService.java index 2ff82c7474..8547556f5d 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/packages/SW360PackageService.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/packages/SW360PackageService.java @@ -19,7 +19,7 @@ import org.apache.thrift.transport.TTransportException; import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.common.SW360Constants; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.AddDocumentRequestStatus; import org.eclipse.sw360.datahandler.thrift.AddDocumentRequestSummary; import org.eclipse.sw360.datahandler.thrift.RequestStatus; @@ -130,10 +130,10 @@ public List searchPackage(String field, String searchQuery, boolean isE if (field.equals("name")) { if (isExactMatch) { - values = values.stream().map(s -> "\"" + s + "\"").map(LuceneAwareDatabaseConnector::prepareWildcardQuery).collect(Collectors.toSet()); + values = values.stream().map(s -> "\"" + s + "\"").map(NouveauLuceneAwareDatabaseConnector::prepareWildcardQuery).collect(Collectors.toSet()); } else { - values = values.stream().map(LuceneAwareDatabaseConnector::prepareWildcardQuery).collect(Collectors.toSet()); + values = values.stream().map(NouveauLuceneAwareDatabaseConnector::prepareWildcardQuery).collect(Collectors.toSet()); } } Map> queryMap = new HashMap<>(); diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/ProjectController.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/ProjectController.java index ca704ab1b2..ed8c3b55f3 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/ProjectController.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/ProjectController.java @@ -44,8 +44,7 @@ import org.eclipse.sw360.datahandler.common.SW360Constants; import org.eclipse.sw360.datahandler.common.SW360Utils; import org.eclipse.sw360.datahandler.common.ThriftEnumUtils; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.permissions.PermissionUtils; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.resourcelists.PaginationParameterException; import org.eclipse.sw360.datahandler.resourcelists.PaginationResult; import org.eclipse.sw360.datahandler.resourcelists.ResourceClassNotFoundException; @@ -289,7 +288,7 @@ public ResponseEntity>> getProjectsForUser( if (CommonUtils.isNotNullEmptyOrWhitespace(name)) { Set values = CommonUtils.splitToSet(name); - values = values.stream().map(LuceneAwareDatabaseConnector::prepareWildcardQuery) + values = values.stream().map(NouveauLuceneAwareDatabaseConnector::prepareWildcardQuery) .collect(Collectors.toSet()); filterMap.put(Project._Fields.NAME.getFieldName(), values); } diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/release/ReleaseController.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/release/ReleaseController.java index b17ddd9f8a..79832e426a 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/release/ReleaseController.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/release/ReleaseController.java @@ -55,7 +55,6 @@ import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.common.SW360Constants; import org.eclipse.sw360.datahandler.common.SW360Utils; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.resourcelists.PaginationParameterException; import org.eclipse.sw360.datahandler.resourcelists.PaginationResult; import org.eclipse.sw360.datahandler.resourcelists.ResourceClassNotFoundException; diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/user/UserController.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/user/UserController.java index 7520288e15..ac8dda5986 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/user/UserController.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/user/UserController.java @@ -22,7 +22,7 @@ import org.apache.thrift.TException; import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.common.SW360Constants; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.resourcelists.ResourceClassNotFoundException; import org.eclipse.sw360.datahandler.resourcelists.PaginationParameterException; import org.eclipse.sw360.datahandler.resourcelists.PaginationResult; @@ -119,13 +119,13 @@ public ResponseEntity>> getUsers( Map> filterMap = new HashMap<>(); if (CommonUtils.isNotNullEmptyOrWhitespace(givenname)) { Set values = CommonUtils.splitToSet(givenname); - values = values.stream().map(LuceneAwareDatabaseConnector::prepareWildcardQuery) + values = values.stream().map(NouveauLuceneAwareDatabaseConnector::prepareWildcardQuery) .collect(Collectors.toSet()); filterMap.put(User._Fields.GIVENNAME.getFieldName(), values); } if (CommonUtils.isNotNullEmptyOrWhitespace(email)) { Set values = CommonUtils.splitToSet(email); - values = values.stream().map(LuceneAwareDatabaseConnector::prepareWildcardQuery) + values = values.stream().map(NouveauLuceneAwareDatabaseConnector::prepareWildcardQuery) .collect(Collectors.toSet()); filterMap.put(User._Fields.EMAIL.getFieldName(), values); } @@ -139,7 +139,7 @@ public ResponseEntity>> getUsers( } if (CommonUtils.isNotNullEmptyOrWhitespace(lastname)) { Set values = CommonUtils.splitToSet(lastname); - values = values.stream().map(LuceneAwareDatabaseConnector::prepareWildcardQuery) + values = values.stream().map(NouveauLuceneAwareDatabaseConnector::prepareWildcardQuery) .collect(Collectors.toSet()); filterMap.put(User._Fields.LASTNAME.getFieldName(), values); } From bc8b5a7f7a413732e22a3a96290573a5d2f47720 Mon Sep 17 00:00:00 2001 From: Gaurav Mishra Date: Wed, 27 Mar 2024 18:06:06 +0530 Subject: [PATCH 02/10] feat(ektorp): remove ektorp from search handlers Signed-off-by: Gaurav Mishra --- .../db/AttachmentUsageRepository.java | 1 - .../datahandler/db/ComponentSearchHandler.java | 3 +-- .../datahandler/db/ModerationSearchHandler.java | 3 +-- .../db/ObligationElementSearchHandler.java | 3 +-- .../db/ObligationListRepository.java | 1 - .../datahandler/db/ObligationSearchHandler.java | 3 +-- .../datahandler/db/PackageSearchHandler.java | 3 +-- .../datahandler/db/ProjectSearchHandler.java | 3 +-- .../ProjectVulnerabilityRatingRepository.java | 1 - .../datahandler/db/ReleaseSearchHandler.java | 3 +-- .../document/SpdxDocumentSearchHandler.java | 3 +-- .../SpdxDocumentCreationInfoSearchHandler.java | 3 +-- .../SpdxPackageInfoSearchHandler.java | 3 +-- .../sw360/components/ComponentHandler.java | 17 ++++++++--------- .../sw360/components/ComponentHandlerTest.java | 4 ++-- .../db/ComponentSearchHandlerTest.java | 2 +- .../eclipse/sw360/licenses/LicenseHandler.java | 10 ++++------ .../db/LicenseObligationListRepository.java | 2 -- .../sw360/moderation/ModerationHandler.java | 2 +- .../eclipse/sw360/packages/PackageHandler.java | 2 +- .../eclipse/sw360/projects/ProjectHandler.java | 9 ++++----- .../sw360/projects/ProjectHandlerTest.java | 2 +- .../org/eclipse/sw360/search/SearchHandler.java | 7 +++---- .../search/Sw360usersDatabaseSearchHandler.java | 5 ++--- .../db/AbstractDatabaseSearchHandler.java | 3 +-- .../search/db/Sw360dbDatabaseSearchHandler.java | 6 ++---- .../eclipse/sw360/search/SearchHandlerTest.java | 2 +- .../org/eclipse/sw360/users/UserHandler.java | 6 ++---- .../sw360/users/db/UserDatabaseHandler.java | 9 --------- .../eclipse/sw360/users/UserHandlerTest.java | 2 +- .../db/RemoteAttachmentDownloader.java | 2 -- .../ComponentAndAttachmentAwareDBTest.java | 2 +- .../nouveau/LuceneAwareCouchDbConnector.java | 1 - pom.xml | 12 ------------ 34 files changed, 45 insertions(+), 95 deletions(-) diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java index a828d3dba0..05efea31c8 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java @@ -23,7 +23,6 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentUsage; -import org.ektorp.support.View; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java index e248bd30f2..48a1d1c2f3 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java @@ -22,7 +22,6 @@ import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; -import org.ektorp.http.HttpClient; import java.io.IOException; import java.util.ArrayList; @@ -81,7 +80,7 @@ public class ComponentSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public ComponentSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { + public ComponentSearchHandler(Supplier cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java index f95c3b049b..e8b31fc119 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java @@ -18,7 +18,6 @@ import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; -import org.ektorp.http.HttpClient; import java.io.IOException; import java.util.List; @@ -66,7 +65,7 @@ public class ModerationSearchHandler { "}")); private final NouveauLuceneAwareDatabaseConnector connector; - public ModerationSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { + public ModerationSearchHandler(Supplier cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java index d1dea9bdcc..7aa9f4cb7d 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java @@ -20,7 +20,6 @@ import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; -import org.ektorp.http.HttpClient; import java.io.IOException; import java.util.List; @@ -52,7 +51,7 @@ public class ObligationElementSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public ObligationElementSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { + public ObligationElementSearchHandler(Supplier cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); // Creates the database connector and adds the lucene search view connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationListRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationListRepository.java index 31a1ca7537..91df3ee8d3 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationListRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationListRepository.java @@ -16,7 +16,6 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.projects.ObligationList; -import org.ektorp.support.View; import com.cloudant.client.api.model.DesignDocument.MapReduce; diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java index dd43dd66b9..b1e2be684c 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java @@ -20,7 +20,6 @@ import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; -import org.ektorp.http.HttpClient; import java.io.IOException; import java.util.List; @@ -48,7 +47,7 @@ public class ObligationSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public ObligationSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { + public ObligationSearchHandler(Supplier cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); // Creates the database connector and adds the lucene search view connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java index 333f247fe9..e7b668f94b 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java @@ -18,7 +18,6 @@ import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; -import org.ektorp.http.HttpClient; import java.io.IOException; import java.util.List; @@ -75,7 +74,7 @@ public class PackageSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public PackageSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { + public PackageSearchHandler(Supplier cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java index 8ab7742a82..99a93920b3 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java @@ -20,7 +20,6 @@ import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; -import org.ektorp.http.HttpClient; import java.io.IOException; import java.util.Collections; @@ -83,7 +82,7 @@ public class ProjectSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public ProjectSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { + public ProjectSearchHandler(Supplier cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectVulnerabilityRatingRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectVulnerabilityRatingRepository.java index 629201caff..b9915a7c3f 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectVulnerabilityRatingRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectVulnerabilityRatingRepository.java @@ -16,7 +16,6 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.ProjectVulnerabilityRating; -import org.ektorp.support.View; import com.cloudant.client.api.model.DesignDocument.MapReduce; diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java index b44f5caeee..f5484e9a1e 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java @@ -18,7 +18,6 @@ import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; -import org.ektorp.http.HttpClient; import java.io.IOException; import java.util.List; @@ -53,7 +52,7 @@ public class ReleaseSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public ReleaseSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { + public ReleaseSearchHandler(Supplier cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java index 0a46d28830..db807b0bb9 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java @@ -19,7 +19,6 @@ import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; -import org.ektorp.http.HttpClient; import java.io.IOException; import java.util.List; @@ -43,7 +42,7 @@ public class SpdxDocumentSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public SpdxDocumentSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { + public SpdxDocumentSearchHandler(Supplier cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java index 3585b5db93..b35e57aee8 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java @@ -19,7 +19,6 @@ import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; -import org.ektorp.http.HttpClient; import java.io.IOException; import java.util.List; @@ -43,7 +42,7 @@ public class SpdxDocumentCreationInfoSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public SpdxDocumentCreationInfoSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { + public SpdxDocumentCreationInfoSearchHandler(Supplier cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java index a9dc71a001..34675e4346 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java @@ -19,7 +19,6 @@ import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; -import org.ektorp.http.HttpClient; import java.io.IOException; import java.util.List; @@ -43,7 +42,7 @@ public class SpdxPackageInfoSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public SpdxPackageInfoSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { + public SpdxPackageInfoSearchHandler(Supplier cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); diff --git a/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java b/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java index cecd8b62a6..5bd508d4ed 100644 --- a/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java +++ b/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java @@ -24,7 +24,6 @@ import org.eclipse.sw360.datahandler.thrift.components.ReleaseNode; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; -import org.ektorp.http.HttpClient; import com.cloudant.client.api.CloudantClient; @@ -50,24 +49,24 @@ public class ComponentHandler implements ComponentService.Iface { private final ReleaseSearchHandler releaseSearchHandler; public ComponentHandler() throws IOException { - this(DatabaseSettings.getConfiguredClient(), DatabaseSettings.getConfiguredHttpClient(), DatabaseSettings.COUCH_DB_DATABASE, DatabaseSettings.COUCH_DB_CHANGE_LOGS, DatabaseSettings.COUCH_DB_ATTACHMENTS); + this(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE, DatabaseSettings.COUCH_DB_CHANGE_LOGS, DatabaseSettings.COUCH_DB_ATTACHMENTS); } - public ComponentHandler(Supplier cClient, Supplier hclient, String dbName, String changeLogsDBName, String attachmentDbName) throws IOException { + public ComponentHandler(Supplier cClient, String dbName, String changeLogsDBName, String attachmentDbName) throws IOException { handler = new ComponentDatabaseHandler(cClient, dbName, changeLogsDBName, attachmentDbName); - componentSearchHandler = new ComponentSearchHandler(hclient, cClient, dbName); - releaseSearchHandler = new ReleaseSearchHandler(hclient, cClient, dbName); + componentSearchHandler = new ComponentSearchHandler(cClient, dbName); + releaseSearchHandler = new ReleaseSearchHandler(cClient, dbName); } // TODO use dependency injection instead of this constructors mess public ComponentHandler(ThriftClients thriftClients) throws IOException { - this(DatabaseSettings.getConfiguredHttpClient(), DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE, DatabaseSettings.COUCH_DB_CHANGE_LOGS, DatabaseSettings.COUCH_DB_ATTACHMENTS, thriftClients); + this(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE, DatabaseSettings.COUCH_DB_CHANGE_LOGS, DatabaseSettings.COUCH_DB_ATTACHMENTS, thriftClients); } - public ComponentHandler(Supplier httpClient, Supplier client, String dbName, String changeLogsDBName, String attachmentDbName, ThriftClients thriftClients) throws IOException { + public ComponentHandler(Supplier client, String dbName, String changeLogsDBName, String attachmentDbName, ThriftClients thriftClients) throws IOException { handler = new ComponentDatabaseHandler(client, dbName, changeLogsDBName, attachmentDbName, thriftClients); - componentSearchHandler = new ComponentSearchHandler(httpClient, client, dbName); - releaseSearchHandler = new ReleaseSearchHandler(httpClient, client, dbName); + componentSearchHandler = new ComponentSearchHandler(client, dbName); + releaseSearchHandler = new ReleaseSearchHandler(client, dbName); } ///////////////////// diff --git a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/ComponentHandlerTest.java b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/ComponentHandlerTest.java index 4e32c19fbe..1be53cac85 100644 --- a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/ComponentHandlerTest.java +++ b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/ComponentHandlerTest.java @@ -44,8 +44,8 @@ public void setUp() throws Exception { assertTestDbNames(); deleteAllDatabases(); componentHandler = new ComponentHandler(DatabaseSettingsTest.getConfiguredClient(), - DatabaseSettingsTest.getConfiguredHttpClient(), DatabaseSettingsTest.COUCH_DB_DATABASE, - DatabaseSettingsTest.COUCH_DB_CHANGELOGS, DatabaseSettingsTest.COUCH_DB_ATTACHMENTS); + DatabaseSettingsTest.COUCH_DB_DATABASE, DatabaseSettingsTest.COUCH_DB_CHANGELOGS, + DatabaseSettingsTest.COUCH_DB_ATTACHMENTS); } @After diff --git a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentSearchHandlerTest.java b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentSearchHandlerTest.java index 23e1bd44e5..2ca0d420a9 100644 --- a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentSearchHandlerTest.java +++ b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentSearchHandlerTest.java @@ -84,7 +84,7 @@ public void setUp() throws Exception { } // Prepare the handler - searchHandler = new ComponentSearchHandler(DatabaseSettingsTest.getConfiguredHttpClient(), DatabaseSettingsTest.getConfiguredClient(), dbName); + searchHandler = new ComponentSearchHandler(DatabaseSettingsTest.getConfiguredClient(), dbName); } @After diff --git a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/LicenseHandler.java b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/LicenseHandler.java index c9dc8cac5f..dddb54fdbb 100644 --- a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/LicenseHandler.java +++ b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/LicenseHandler.java @@ -23,14 +23,12 @@ import org.eclipse.sw360.datahandler.thrift.users.UserGroup; import org.eclipse.sw360.licenses.db.LicenseDatabaseHandler; import org.eclipse.sw360.datahandler.db.ObligationElementSearchHandler; -import org.ektorp.http.HttpClient; import com.cloudant.client.api.CloudantClient; import org.apache.thrift.TException; import java.nio.ByteBuffer; -import java.net.MalformedURLException; import java.util.List; import java.util.Set; import java.util.function.Supplier; @@ -49,13 +47,13 @@ public class LicenseHandler implements LicenseService.Iface { ObligationElementSearchHandler searchHandler; ObligationSearchHandler obligationSearchHandler; - LicenseHandler() throws MalformedURLException, IOException { + LicenseHandler() throws IOException { handler = new LicenseDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE); - searchHandler = new ObligationElementSearchHandler(DatabaseSettings.getConfiguredHttpClient(), DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE); - obligationSearchHandler = new ObligationSearchHandler(DatabaseSettings.getConfiguredHttpClient(), DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE); + searchHandler = new ObligationElementSearchHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE); + obligationSearchHandler = new ObligationSearchHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE); } - LicenseHandler(Supplier httpClient, String dbName) throws MalformedURLException, IOException { + LicenseHandler(Supplier httpClient, String dbName) throws IOException { handler = new LicenseDatabaseHandler(httpClient, dbName); } diff --git a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseObligationListRepository.java b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseObligationListRepository.java index e4277b3f34..0fbcf655dc 100644 --- a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseObligationListRepository.java +++ b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseObligationListRepository.java @@ -17,8 +17,6 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.licenses.LicenseObligationList; -import org.ektorp.support.View; - import com.cloudant.client.api.model.DesignDocument.MapReduce; /** diff --git a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/ModerationHandler.java b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/ModerationHandler.java index 735b4c3908..07cf1bcdf1 100644 --- a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/ModerationHandler.java +++ b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/ModerationHandler.java @@ -54,7 +54,7 @@ public class ModerationHandler implements ModerationService.Iface { public ModerationHandler() throws IOException { handler = new ModerationDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE, DatabaseSettings.COUCH_DB_ATTACHMENTS); - modSearchHandler = new ModerationSearchHandler(DatabaseSettings.getConfiguredHttpClient(), DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE); + modSearchHandler = new ModerationSearchHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE); } @Override diff --git a/backend/src/src-packages/src/main/java/org/eclipse/sw360/packages/PackageHandler.java b/backend/src/src-packages/src/main/java/org/eclipse/sw360/packages/PackageHandler.java index 2db47dccc1..18d6bc4921 100644 --- a/backend/src/src-packages/src/main/java/org/eclipse/sw360/packages/PackageHandler.java +++ b/backend/src/src-packages/src/main/java/org/eclipse/sw360/packages/PackageHandler.java @@ -43,7 +43,7 @@ public class PackageHandler implements PackageService.Iface { PackageHandler() throws IOException { handler = new PackageDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE, DatabaseSettings.COUCH_DB_CHANGE_LOGS, DatabaseSettings.COUCH_DB_ATTACHMENTS); - packageSearchHandler = new PackageSearchHandler(DatabaseSettings.getConfiguredHttpClient(), DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE); + packageSearchHandler = new PackageSearchHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE); } @Override diff --git a/backend/src/src-projects/src/main/java/org/eclipse/sw360/projects/ProjectHandler.java b/backend/src/src-projects/src/main/java/org/eclipse/sw360/projects/ProjectHandler.java index fdfdb391ba..d54adee445 100644 --- a/backend/src/src-projects/src/main/java/org/eclipse/sw360/projects/ProjectHandler.java +++ b/backend/src/src-projects/src/main/java/org/eclipse/sw360/projects/ProjectHandler.java @@ -41,7 +41,6 @@ import org.eclipse.sw360.datahandler.thrift.projects.ProjectService; import org.eclipse.sw360.datahandler.thrift.projects.UsedReleaseRelations; import org.eclipse.sw360.datahandler.thrift.users.User; -import org.ektorp.http.HttpClient; import com.cloudant.client.api.CloudantClient; @@ -67,17 +66,17 @@ public class ProjectHandler implements ProjectService.Iface { ProjectHandler() throws IOException { handler = new ProjectDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE, DatabaseSettings.COUCH_DB_ATTACHMENTS); - searchHandler = new ProjectSearchHandler(DatabaseSettings.getConfiguredHttpClient(), DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE); + searchHandler = new ProjectSearchHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE); } ProjectHandler(Supplier httpClient, String dbName, String attchmntDbName) throws IOException { handler = new ProjectDatabaseHandler(httpClient, dbName, attchmntDbName); - searchHandler = new ProjectSearchHandler(DatabaseSettings.getConfiguredHttpClient(), DatabaseSettings.getConfiguredClient(), dbName); + searchHandler = new ProjectSearchHandler(DatabaseSettings.getConfiguredClient(), dbName); } - ProjectHandler(Supplier cClient,Supplier hClient, String dbName, String changeLogsDbName, String attchmntDbName) throws IOException { + ProjectHandler(Supplier cClient, String dbName, String changeLogsDbName, String attchmntDbName) throws IOException { handler = new ProjectDatabaseHandler(cClient, dbName, changeLogsDbName, attchmntDbName); - searchHandler = new ProjectSearchHandler(hClient, cClient, dbName); + searchHandler = new ProjectSearchHandler(cClient, dbName); } ///////////////////// diff --git a/backend/src/src-projects/src/test/java/org/eclipse/sw360/projects/ProjectHandlerTest.java b/backend/src/src-projects/src/test/java/org/eclipse/sw360/projects/ProjectHandlerTest.java index 3534e2e938..9c3a3b18aa 100644 --- a/backend/src/src-projects/src/test/java/org/eclipse/sw360/projects/ProjectHandlerTest.java +++ b/backend/src/src-projects/src/test/java/org/eclipse/sw360/projects/ProjectHandlerTest.java @@ -75,7 +75,7 @@ public void setUp() throws Exception { } // Create the connector - handler = new ProjectHandler(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.getConfiguredHttpClient(), dbName, changeLogDbName, attachmentDbName); + handler = new ProjectHandler(DatabaseSettingsTest.getConfiguredClient(), dbName, changeLogDbName, attachmentDbName); } @After diff --git a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/SearchHandler.java b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/SearchHandler.java index 79d22c8ab2..b486185508 100644 --- a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/SearchHandler.java +++ b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/SearchHandler.java @@ -23,7 +23,6 @@ import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.search.db.AbstractDatabaseSearchHandler; import org.eclipse.sw360.search.db.Sw360dbDatabaseSearchHandler; -import org.ektorp.http.HttpClient; import java.io.IOException; import java.util.ArrayList; @@ -55,9 +54,9 @@ public SearchHandler() throws IOException { dbSw360users = new Sw360usersDatabaseSearchHandler(); } - public SearchHandler(Supplier hclient, Supplier cclient, String dbName) throws IOException { - dbSw360db = new Sw360dbDatabaseSearchHandler(hclient, cclient, dbName); - dbSw360users = new Sw360usersDatabaseSearchHandler(hclient, cclient, dbName); + public SearchHandler(Supplier cclient, String dbName) throws IOException { + dbSw360db = new Sw360dbDatabaseSearchHandler(cclient, dbName); + dbSw360users = new Sw360usersDatabaseSearchHandler(cclient, dbName); } @Override diff --git a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/Sw360usersDatabaseSearchHandler.java b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/Sw360usersDatabaseSearchHandler.java index 09fad9dd42..f503f0d2d5 100644 --- a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/Sw360usersDatabaseSearchHandler.java +++ b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/Sw360usersDatabaseSearchHandler.java @@ -13,7 +13,6 @@ import org.eclipse.sw360.datahandler.thrift.search.SearchResult; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.search.db.AbstractDatabaseSearchHandler; -import org.ektorp.http.HttpClient; import com.cloudant.client.api.CloudantClient; @@ -26,8 +25,8 @@ public Sw360usersDatabaseSearchHandler() throws IOException { super(DatabaseSettings.COUCH_DB_USERS); } - public Sw360usersDatabaseSearchHandler(Supplier hclient, Supplier client, String dbName) throws IOException { - super(hclient, client, dbName); + public Sw360usersDatabaseSearchHandler(Supplier client, String dbName) throws IOException { + super(client, dbName); } @Override diff --git a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java index b812708a01..ec466286ec 100644 --- a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java +++ b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java @@ -25,7 +25,6 @@ import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexDesignDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauIndexFunction; -import org.ektorp.http.HttpClient; import org.jetbrains.annotations.NotNull; import java.io.IOException; @@ -98,7 +97,7 @@ public AbstractDatabaseSearchHandler(String dbName) throws IOException { connector.addDesignDoc(searchView); } - public AbstractDatabaseSearchHandler(Supplier client, Supplier cClient, String dbName) throws IOException { + public AbstractDatabaseSearchHandler(Supplier cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); // Create the database connector and add the search view to couchDB connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); diff --git a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/Sw360dbDatabaseSearchHandler.java b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/Sw360dbDatabaseSearchHandler.java index f43750f2b9..930ec7d6af 100644 --- a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/Sw360dbDatabaseSearchHandler.java +++ b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/Sw360dbDatabaseSearchHandler.java @@ -13,7 +13,6 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettings; import org.eclipse.sw360.datahandler.common.SW360Constants; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.db.ComponentRepository; import org.eclipse.sw360.datahandler.db.ProjectRepository; import org.eclipse.sw360.datahandler.db.ReleaseRepository; @@ -26,7 +25,6 @@ import org.eclipse.sw360.datahandler.thrift.projects.Project; import org.eclipse.sw360.datahandler.thrift.search.SearchResult; import org.eclipse.sw360.datahandler.thrift.users.User; -import org.ektorp.http.HttpClient; import com.cloudant.client.api.CloudantClient; @@ -52,8 +50,8 @@ public Sw360dbDatabaseSearchHandler() throws IOException { componentRepository = new ComponentRepository(db, releaseRepository, vendorRepository); } - public Sw360dbDatabaseSearchHandler(Supplier client, Supplier cclient, String dbName) throws IOException { - super(client, cclient, dbName); + public Sw360dbDatabaseSearchHandler(Supplier cclient, String dbName) throws IOException { + super(cclient, dbName); DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cclient, dbName); diff --git a/backend/src/src-search/src/test/java/org/eclipse/sw360/search/SearchHandlerTest.java b/backend/src/src-search/src/test/java/org/eclipse/sw360/search/SearchHandlerTest.java index cfebcd2bd0..24da0cb98a 100644 --- a/backend/src/src-search/src/test/java/org/eclipse/sw360/search/SearchHandlerTest.java +++ b/backend/src/src-search/src/test/java/org/eclipse/sw360/search/SearchHandlerTest.java @@ -23,7 +23,7 @@ public class SearchHandlerTest { @Before public void setUp() throws Exception { - handler = new SearchHandler(DatabaseSettingsTest.getConfiguredHttpClient(), DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.COUCH_DB_DATABASE); + handler = new SearchHandler(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.COUCH_DB_DATABASE); } @Test(expected = TException.class) diff --git a/backend/src/src-users/src/main/java/org/eclipse/sw360/users/UserHandler.java b/backend/src/src-users/src/main/java/org/eclipse/sw360/users/UserHandler.java index 0d135e339a..160cede59d 100644 --- a/backend/src/src-users/src/main/java/org/eclipse/sw360/users/UserHandler.java +++ b/backend/src/src-users/src/main/java/org/eclipse/sw360/users/UserHandler.java @@ -26,10 +26,8 @@ import org.eclipse.sw360.users.db.UserDatabaseHandler; import org.eclipse.sw360.users.util.FileUtil; import org.eclipse.sw360.users.util.ReadFileDepartmentConfig; -import org.ektorp.http.HttpClient; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.util.*; import java.util.function.Supplier; @@ -56,8 +54,8 @@ public UserHandler() throws IOException { readFileDepartmentConfig = new ReadFileDepartmentConfig(); } - public UserHandler(Supplier client, Supplier httpclient, String userDbName) throws IOException { - db = new UserDatabaseHandler(client, httpclient, userDbName); + public UserHandler(Supplier client, String userDbName) throws IOException { + db = new UserDatabaseHandler(client, userDbName); } @Override diff --git a/backend/src/src-users/src/main/java/org/eclipse/sw360/users/db/UserDatabaseHandler.java b/backend/src/src-users/src/main/java/org/eclipse/sw360/users/db/UserDatabaseHandler.java index 15f2363447..79f9299581 100644 --- a/backend/src/src-users/src/main/java/org/eclipse/sw360/users/db/UserDatabaseHandler.java +++ b/backend/src/src-users/src/main/java/org/eclipse/sw360/users/db/UserDatabaseHandler.java @@ -32,7 +32,6 @@ import org.eclipse.sw360.datahandler.thrift.users.UserGroup; import org.eclipse.sw360.users.util.FileUtil; import org.eclipse.sw360.users.util.ReadFileDepartmentConfig; -import org.ektorp.http.HttpClient; import java.io.*; import java.text.DateFormat; @@ -78,14 +77,6 @@ public UserDatabaseHandler(Supplier httpClient, String dbName) t userSearchHandler = new UserSearchHandler(DatabaseSettings.getConfiguredClient(), dbName); } - public UserDatabaseHandler(Supplier httpClient, Supplier client, String dbName) throws IOException { - // Create the connector - db = new DatabaseConnectorCloudant(httpClient, dbName); - dbConnector = new DatabaseConnector(client, dbName); - repository = new UserRepository(db); - userSearchHandler = new UserSearchHandler(httpClient, dbName); - } - public User getByEmail(String email) { return repository.getByEmail(email); } diff --git a/backend/src/src-users/src/test/java/org/eclipse/sw360/users/UserHandlerTest.java b/backend/src/src-users/src/test/java/org/eclipse/sw360/users/UserHandlerTest.java index af3fb2284a..5057143c3f 100644 --- a/backend/src/src-users/src/test/java/org/eclipse/sw360/users/UserHandlerTest.java +++ b/backend/src/src-users/src/test/java/org/eclipse/sw360/users/UserHandlerTest.java @@ -40,7 +40,7 @@ public void setUp() throws Exception { TestUtils.createDatabase(DatabaseSettingsTest.getConfiguredClient(), dbName); // Create the connector - handler = new UserHandler(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.getConfiguredHttpClient(), dbName); + handler = new UserHandler(DatabaseSettingsTest.getConfiguredClient(), dbName); } @After diff --git a/backend/utils/src/main/java/org/eclipse/sw360/attachments/db/RemoteAttachmentDownloader.java b/backend/utils/src/main/java/org/eclipse/sw360/attachments/db/RemoteAttachmentDownloader.java index b130b7f28f..60e58a991a 100644 --- a/backend/utils/src/main/java/org/eclipse/sw360/attachments/db/RemoteAttachmentDownloader.java +++ b/backend/utils/src/main/java/org/eclipse/sw360/attachments/db/RemoteAttachmentDownloader.java @@ -15,11 +15,9 @@ import org.eclipse.sw360.datahandler.common.DatabaseSettings; import org.eclipse.sw360.datahandler.common.Duration; import org.eclipse.sw360.datahandler.couchdb.AttachmentConnector; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.db.AttachmentContentRepository; import org.eclipse.sw360.datahandler.thrift.SW360Exception; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; -import org.ektorp.http.HttpClient; import com.cloudant.client.api.CloudantClient; diff --git a/libraries/importers/src/test/java/org/eclipse/sw360/importer/ComponentAndAttachmentAwareDBTest.java b/libraries/importers/src/test/java/org/eclipse/sw360/importer/ComponentAndAttachmentAwareDBTest.java index 2f453e80e3..4f6a235a77 100644 --- a/libraries/importers/src/test/java/org/eclipse/sw360/importer/ComponentAndAttachmentAwareDBTest.java +++ b/libraries/importers/src/test/java/org/eclipse/sw360/importer/ComponentAndAttachmentAwareDBTest.java @@ -81,7 +81,7 @@ protected static ThriftClients getThriftClients() throws TException, IOException ThriftClients thriftClients = failingMock(ThriftClients.class); - ComponentHandler componentHandler = new ComponentHandler(DatabaseSettingsTest.getConfiguredHttpClient(),DatabaseSettingsTest.getConfiguredClient(),DatabaseSettingsTest.COUCH_DB_DATABASE, DatabaseSettingsTest.COUCH_DB_CHANGELOGS, DatabaseSettingsTest.COUCH_DB_ATTACHMENTS, thriftClients); + ComponentHandler componentHandler = new ComponentHandler(DatabaseSettingsTest.getConfiguredClient(),DatabaseSettingsTest.COUCH_DB_DATABASE, DatabaseSettingsTest.COUCH_DB_CHANGELOGS, DatabaseSettingsTest.COUCH_DB_ATTACHMENTS, thriftClients); VendorHandler vendorHandler = new VendorHandler(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.getConfiguredHttpClient(), DatabaseSettingsTest.COUCH_DB_DATABASE); AttachmentHandler attachmentHandler = new AttachmentHandler(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.COUCH_DB_DATABASE, DatabaseSettingsTest.COUCH_DB_ATTACHMENTS); diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java index 1a13032740..39fb45982a 100644 --- a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java @@ -14,7 +14,6 @@ import com.cloudant.client.internal.DatabaseURIHelper; import com.cloudant.client.org.lightcouch.CouchDbException; import com.cloudant.client.org.lightcouch.internal.CouchDbUtil; -import com.cloudant.http.HttpConnection; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; diff --git a/pom.xml b/pom.xml index f6faf5bdd9..fcb2f2c873 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,6 @@ 1.10.0 2.1.3 1.5.0 - 0.2.0 1.0.1 1.3.9-1 2.3.9 @@ -300,17 +299,6 @@ commons-collections4 ${commons-collection4.version} - - com.github.ldriscoll - ektorplucene - ${ektorplucene.version} - - - net.sourceforge.findbugs - annotations - - - com.github.stephenc.findbugs From 2a477ee4bcaa38ca0657efde2df0019392e4d6cc Mon Sep 17 00:00:00 2001 From: Gaurav Mishra Date: Thu, 4 Apr 2024 17:23:52 +0530 Subject: [PATCH 03/10] chore(couchdb-lucene): remove third-party/couchdb-lucene Signed-off-by: Gaurav Mishra --- .github/testForLicenseHeaders.sh | 1 - .../resources/couchdb.properties | 12 +- .../test-resources/couchdb-test.properties | 1 - libraries/datahandler/pom.xml | 4 - .../datahandler/common/DatabaseSettings.java | 2 - .../common/DatabaseSettingsTest.java | 2 - libraries/nouveau-handler/pom.xml | 72 +- pom.xml | 5 - scripts/docker-config/couchdb-lucene.ini | 37 - .../docker-config/couchdb.properties.template | 9 +- scripts/patches/couchdb-lucene.patch | 98 -- third-party/couchdb-lucene/.travis.yml | 7 - third-party/couchdb-lucene/BREAKING_CHANGES | 11 - .../couchdb-lucene/CONFIGURING_ANALYZERS.md | 134 -- third-party/couchdb-lucene/LICENSE | 176 --- third-party/couchdb-lucene/NEWS | 107 -- third-party/couchdb-lucene/README.md | 681 -------- third-party/couchdb-lucene/THANKS.md | 5 - third-party/couchdb-lucene/TODO | 22 - .../couchdb-lucene/couchdb-external-hook.py | 106 -- third-party/couchdb-lucene/pom.xml | 282 ---- .../couchdb-lucene/src/main/assembly/dist.xml | 74 - .../github/rnewson/couchdb/lucene/Config.java | 72 - .../couchdb/lucene/CustomQueryParser.java | 130 -- .../couchdb/lucene/DatabaseIndexer.java | 868 ---------- .../couchdb/lucene/DocumentConverter.java | 155 -- .../couchdb/lucene/HttpClientFactory.java | 182 --- .../rnewson/couchdb/lucene/IndexKey.java | 92 -- .../couchdb/lucene/JSONErrorHandler.java | 55 - .../rnewson/couchdb/lucene/LuceneServlet.java | 242 --- .../github/rnewson/couchdb/lucene/Main.java | 69 - .../rnewson/couchdb/lucene/PathParts.java | 81 - .../rnewson/couchdb/lucene/QueryPlan.java | 108 -- .../github/rnewson/couchdb/lucene/Tika.java | 97 -- .../rnewson/couchdb/lucene/TypedField.java | 77 - .../rnewson/couchdb/lucene/couchdb/Couch.java | 55 - .../couchdb/lucene/couchdb/CouchDocument.java | 56 - .../couchdb/lucene/couchdb/Database.java | 212 --- .../couchdb/lucene/couchdb/DatabaseInfo.java | 38 - .../lucene/couchdb/DesignDocument.java | 67 - .../couchdb/lucene/couchdb/FieldType.java | 240 --- .../couchdb/lucene/couchdb/HttpUtils.java | 62 - .../lucene/couchdb/UpdateSequence.java | 231 --- .../rnewson/couchdb/lucene/couchdb/View.java | 122 -- .../couchdb/lucene/couchdb/ViewSettings.java | 78 - .../rnewson/couchdb/lucene/rhino/JSLog.java | 58 - .../couchdb/lucene/rhino/RhinoDocument.java | 182 --- .../couchdb/lucene/util/Analyzers.java | 544 ------- .../couchdb/lucene/util/Constants.java | 41 - .../util/ErrorPreservingResponseHandler.java | 42 - .../couchdb/lucene/util/ServletUtils.java | 114 -- .../util/StatusCodeResponseHandler.java | 36 - .../couchdb/lucene/util/StopWatch.java | 38 - .../rnewson/couchdb/lucene/util/Utils.java | 80 - .../src/main/resources/couchdb-lucene.ini | 26 - .../src/main/tools/etc/init.d/couchdb-lucene | 160 -- .../src/main/webapp/WEB-INF/web.xml | 38 - .../rnewson/couchdb/lucene/ConfigTest.java | 77 - .../couchdb/lucene/CustomQueryParserTest.java | 115 -- .../couchdb/lucene/DocumentConverterTest.java | 333 ---- .../rnewson/couchdb/lucene/JsonTest.java | 30 - .../rnewson/couchdb/lucene/PathPartsTest.java | 58 - .../rnewson/couchdb/lucene/TikaTest.java | 66 - .../lucene/couchdb/CouchDocumentTest.java | 45 - .../lucene/couchdb/DesignDocumentTest.java | 56 - .../lucene/couchdb/UpdateSequenceTest.java | 50 - .../couchdb/lucene/couchdb/ViewTest.java | 37 - .../couchdb/lucene/util/AnalyzersTest.java | 165 -- .../couchdb/lucene/util/UtilsTest.java | 36 - .../src/test/resources/couchdb-lucene.ini | 8 - .../src/test/resources/example.doc | 1389 ----------------- .../src/test/resources/example.xml | 23 - .../src/test/resources/paxos-simple.pdf | Bin 95019 -> 0 bytes third-party/pom.xml | 48 - 74 files changed, 70 insertions(+), 9062 deletions(-) delete mode 100644 scripts/docker-config/couchdb-lucene.ini delete mode 100644 scripts/patches/couchdb-lucene.patch delete mode 100644 third-party/couchdb-lucene/.travis.yml delete mode 100644 third-party/couchdb-lucene/BREAKING_CHANGES delete mode 100644 third-party/couchdb-lucene/CONFIGURING_ANALYZERS.md delete mode 100644 third-party/couchdb-lucene/LICENSE delete mode 100644 third-party/couchdb-lucene/NEWS delete mode 100644 third-party/couchdb-lucene/README.md delete mode 100644 third-party/couchdb-lucene/THANKS.md delete mode 100644 third-party/couchdb-lucene/TODO delete mode 100755 third-party/couchdb-lucene/couchdb-external-hook.py delete mode 100644 third-party/couchdb-lucene/pom.xml delete mode 100644 third-party/couchdb-lucene/src/main/assembly/dist.xml delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/Config.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/CustomQueryParser.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/DatabaseIndexer.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/DocumentConverter.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/HttpClientFactory.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/IndexKey.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/JSONErrorHandler.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/LuceneServlet.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/Main.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/PathParts.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/QueryPlan.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/Tika.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/TypedField.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/Couch.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/CouchDocument.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/Database.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/DatabaseInfo.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/DesignDocument.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/FieldType.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/HttpUtils.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/UpdateSequence.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/View.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/ViewSettings.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/rhino/JSLog.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/rhino/RhinoDocument.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/Analyzers.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/Constants.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/ErrorPreservingResponseHandler.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/ServletUtils.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/StatusCodeResponseHandler.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/StopWatch.java delete mode 100644 third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/Utils.java delete mode 100644 third-party/couchdb-lucene/src/main/resources/couchdb-lucene.ini delete mode 100644 third-party/couchdb-lucene/src/main/tools/etc/init.d/couchdb-lucene delete mode 100644 third-party/couchdb-lucene/src/main/webapp/WEB-INF/web.xml delete mode 100644 third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/ConfigTest.java delete mode 100644 third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/CustomQueryParserTest.java delete mode 100644 third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/DocumentConverterTest.java delete mode 100644 third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/JsonTest.java delete mode 100644 third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/PathPartsTest.java delete mode 100644 third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/TikaTest.java delete mode 100644 third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/CouchDocumentTest.java delete mode 100644 third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/DesignDocumentTest.java delete mode 100644 third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/UpdateSequenceTest.java delete mode 100644 third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/ViewTest.java delete mode 100644 third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/util/AnalyzersTest.java delete mode 100644 third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/util/UtilsTest.java delete mode 100644 third-party/couchdb-lucene/src/test/resources/couchdb-lucene.ini delete mode 100644 third-party/couchdb-lucene/src/test/resources/example.doc delete mode 100644 third-party/couchdb-lucene/src/test/resources/example.xml delete mode 100644 third-party/couchdb-lucene/src/test/resources/paxos-simple.pdf delete mode 100644 third-party/pom.xml diff --git a/.github/testForLicenseHeaders.sh b/.github/testForLicenseHeaders.sh index 58a4c8136b..cbaa3ec278 100755 --- a/.github/testForLicenseHeaders.sh +++ b/.github/testForLicenseHeaders.sh @@ -46,7 +46,6 @@ done <<< "$(git ls-files \ | grep -v 'sw360.code-workspace' \ | grep -v 'default_secrets' \ | grep -v 'requirements.txt' \ - | grep -Ev 'third-party/couchdb-lucene/*' \ | grep -Ev '*/asciidoc/*' \ | grep -Ev '.vscode/*' \ | grep -Ev 'config/*')" \ diff --git a/build-configuration/resources/couchdb.properties b/build-configuration/resources/couchdb.properties index a92b7700e2..e40f91d235 100644 --- a/build-configuration/resources/couchdb.properties +++ b/build-configuration/resources/couchdb.properties @@ -20,14 +20,8 @@ couchdb.change_logs = sw360changelogs couchdb.config = sw360config couchdb.vulnerability_management = sw360vm lucenesearch.limit = 150 -couchdb.lucene.url = http://localhost:8080/couchdb-lucene -# Warning: If you enable lucene leading wildcards you have to enable this configuration also in couchdb-lucene.ini -# leading wildcard search is disabled as default because its a expensive operation. -# couchdb-lucene.ini (is part of the couchdb-lucene .war package) -# [lucene] -# allowLeadingWildcard=true +# FIXME: Look for alternatives +# Warning: Nouveau currently does not support use of leading wildcards. # see more: https://wiki.apache.org/lucene-java/LuceneFAQ#What_wildcard_search_support_is_available_from_Lucene.3F -lucenesearch.leading.wildcard = true - - +lucenesearch.leading.wildcard = false diff --git a/build-configuration/test-resources/couchdb-test.properties b/build-configuration/test-resources/couchdb-test.properties index 4bc303fb06..e2907e58cd 100644 --- a/build-configuration/test-resources/couchdb-test.properties +++ b/build-configuration/test-resources/couchdb-test.properties @@ -18,4 +18,3 @@ couchdb.usersdb = sw360_test_users couchdb.attachments = sw360_test_attachments couchdb.config = sw360_test_config couchdb.vulnerability_management = sw360_test_vm -couchdb.lucene.url = http://localhost:8080/couchdb-lucene diff --git a/libraries/datahandler/pom.xml b/libraries/datahandler/pom.xml index d24838235c..78d505d2d8 100644 --- a/libraries/datahandler/pom.xml +++ b/libraries/datahandler/pom.xml @@ -188,10 +188,6 @@ org.jetbrains annotations - - com.github.ldriscoll - ektorplucene - org.ektorp org.ektorp diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/DatabaseSettings.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/DatabaseSettings.java index 80e41e3dc6..e7e1c3fba1 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/DatabaseSettings.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/DatabaseSettings.java @@ -37,7 +37,6 @@ public class DatabaseSettings { public static final String PROPERTIES_FILE_PATH = "/couchdb.properties"; public static final String COUCH_DB_URL; - public static final String COUCH_DB_LUCENE_URL; public static final String COUCH_DB_DATABASE; public static final String COUCH_DB_ATTACHMENTS; public static final String COUCH_DB_CHANGE_LOGS; @@ -65,7 +64,6 @@ public class DatabaseSettings { .ofNullable(System.getenv("COUCHDB_PASSWORD") != null ? System.getenv("COUCHDB_PASSWORD") : props.getProperty("couchdb.password", "")); COUCH_DB_DATABASE = props.getProperty("couchdb.database", "sw360db"); - COUCH_DB_LUCENE_URL = props.getProperty("couchdb.lucene.url", "http://localhost:8080/couchdb-lucene"); COUCH_DB_ATTACHMENTS = props.getProperty("couchdb.attachments", "sw360attachments"); COUCH_DB_CHANGE_LOGS = props.getProperty("couchdb.change_logs", "sw360changelogs"); COUCH_DB_CONFIG = props.getProperty("couchdb.config", "sw360config"); diff --git a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/common/DatabaseSettingsTest.java b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/common/DatabaseSettingsTest.java index eedc3dc46d..8a335aff6a 100644 --- a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/common/DatabaseSettingsTest.java +++ b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/common/DatabaseSettingsTest.java @@ -33,7 +33,6 @@ public class DatabaseSettingsTest { public static final String PROPERTIES_FILE_PATH = "/couchdb-test.properties"; public static final String COUCH_DB_URL; - public static final String COUCH_DB_LUCENE_URL; public static final String COUCH_DB_DATABASE; public static final String COUCH_DB_ATTACHMENTS; public static final String COUCH_DB_CONFIG; @@ -48,7 +47,6 @@ public class DatabaseSettingsTest { Properties props = CommonUtils.loadProperties(DatabaseSettingsTest.class, PROPERTIES_FILE_PATH); COUCH_DB_URL = props.getProperty("couchdb.url", "http://localhost:5984"); - COUCH_DB_LUCENE_URL = props.getProperty("couchdb.lucene.url", "http://localhost:8080/couchdb-lucene"); COUCH_DB_DATABASE = props.getProperty("couchdb.database", "sw360_test_db"); COUCH_DB_USERNAME = props.getProperty("couchdb.user", ""); COUCH_DB_PASSWORD = props.getProperty("couchdb.password", ""); diff --git a/libraries/nouveau-handler/pom.xml b/libraries/nouveau-handler/pom.xml index 8b543fd347..c01a62a48b 100644 --- a/libraries/nouveau-handler/pom.xml +++ b/libraries/nouveau-handler/pom.xml @@ -16,7 +16,7 @@ libraries org.eclipse.sw360 - ${revision} + 18.99.1 nouveau-handler @@ -24,21 +24,76 @@ jar - ${liferay.deploy.dir} + ${jars.deploy.dir} org.eclipse.sw360.nouveau-handler-${project.version} + + org.apache.thrift.tools + maven-thrift-plugin + 0.1.11 + + thrift + java:generated_annotations=suppress + true + + + + + thrift-sources + generate-sources + + compile + + + + + thrift-test-sources + generate-test-sources + + testCompile + + + + + + org.codehaus.mojo + build-helper-maven-plugin + ${build-helper-maven-plugin.version} + + + thrift-sources + generate-sources + + add-source + + + + target/generated-sources/thrift + + + + + thrift-test-sources + generate-test-sources + + add-test-source + + + + target/generated-test-sources/thrift + + + + + org.apache.maven.plugins maven-source-plugin ${maven-source-plugin.version} - - biz.aQute.bnd - bnd-maven-plugin - org.apache.maven.plugins maven-jar-plugin @@ -52,7 +107,10 @@ - ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + true + org.eclipse.sw360.nouveau + diff --git a/pom.xml b/pom.xml index fcb2f2c873..0b363cba1f 100644 --- a/pom.xml +++ b/pom.xml @@ -335,11 +335,6 @@ httpcore ${httpcore.version} - - org.apache.httpcomponents.client5 - httpclient5 - ${httpclient5.version} - javax.activation activation diff --git a/scripts/docker-config/couchdb-lucene.ini b/scripts/docker-config/couchdb-lucene.ini deleted file mode 100644 index 9f27e2b04c..0000000000 --- a/scripts/docker-config/couchdb-lucene.ini +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright Siemens AG, 2013-2015. Part of the SW360 Portal Project. -# -# Copying and distribution of this file, with or without modification, -# are permitted in any medium without royalty provided the copyright -# notice and this notice are preserved. This file is offered as-is, -# without any warranty. -# -# SPDX-License-Identifier: EPL-2.0 -# -# modified file for couchdb lucene configuration - -[lucene] -# The output directory for Lucene indexes. -dir=indexes - -# The local host name that couchdb-lucene binds to -host=${COUCHDB_HOST} - -# The port that couchdb-lucene binds to. -port=5985 - -# Timeout for requests in milliseconds. -timeout=10000 - -# Timeout for changes requests. -# changes_timeout=60000 - -# Default limit for search results -limit=25 - -# Allow leading wildcard? -allowLeadingWildcard=true - -# couchdb server mappings - -[local] -url = http://${COUCHDB_USER}:${COUCHDB_PASSWORD}@${COUCHDB_HOST}:5984 diff --git a/scripts/docker-config/couchdb.properties.template b/scripts/docker-config/couchdb.properties.template index e8a19a43d3..8af452c1a2 100644 --- a/scripts/docker-config/couchdb.properties.template +++ b/scripts/docker-config/couchdb.properties.template @@ -18,12 +18,7 @@ couchdb.change_logs = sw360changelogs couchdb.config = sw360config couchdb.vulnerability_management = sw360vm lucenesearch.limit = 1000 -couchdb.lucene.url = http://localhost:8080/couchdb-lucene -# Warning: If you enable lucene leading wildcards you have to enable this configuration also in couchdb-lucene.ini -# leading wildcard search is disabled as default because its a expensive operation. -# couchdb-lucene.ini (is part of the couchdb-lucene .war package) -# [lucene] -# allowLeadingWildcard=true +# Warning: Nouveau currently does not support use of leading wildcards. # see more: https://wiki.apache.org/lucene-java/LuceneFAQ#What_wildcard_search_support_is_available_from_Lucene.3F -lucenesearch.leading.wildcard = true \ No newline at end of file +lucenesearch.leading.wildcard = false \ No newline at end of file diff --git a/scripts/patches/couchdb-lucene.patch b/scripts/patches/couchdb-lucene.patch deleted file mode 100644 index dfab4e92f3..0000000000 --- a/scripts/patches/couchdb-lucene.patch +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright Siemens AG, 2013-2015. Part of the SW360 Portal Project. -# -# Copying and distribution of this file, with or without modification, -# are permitted in any medium without royalty provided the copyright -# notice and this notice are preserved. This file is offered as-is, -# without any warranty. -# -# SPDX-License-Identifier: EPL-2.0 -# -From a332d09f9045ce3976d71823ebd9a4fe4a736cb3 Mon Sep 17 00:00:00 2001 -From: Birgit Heydenreich -Date: Sat, 7 Nov 2015 15:31:24 +0100 -Subject: [PATCH] patched with context pull request jalpedersen - - ---- - .../java/com/github/rnewson/couchdb/lucene/LuceneServlet.java | 9 ++++++--- - src/main/java/com/github/rnewson/couchdb/lucene/PathParts.java | 4 +++- - .../com/github/rnewson/couchdb/lucene/util/ServletUtils.java | 6 ++++++ - 3 files changed, 15 insertions(+), 4 deletions(-) - -diff --git a/src/main/java/com/github/rnewson/couchdb/lucene/LuceneServlet.java b/src/main/java/com/github/rnewson/couchdb/lucene/LuceneServlet.java -index ecb200d..9426e58 100644 ---- a/src/main/java/com/github/rnewson/couchdb/lucene/LuceneServlet.java -+++ b/src/main/java/com/github/rnewson/couchdb/lucene/LuceneServlet.java -@@ -41,6 +41,8 @@ - import java.io.IOException; - import java.util.*; - -+import static com.github.rnewson.couchdb.lucene.util.ServletUtils.getUri; -+ - public final class LuceneServlet extends HttpServlet { - - private static final Logger LOG = Logger.getLogger(LuceneServlet.class); -@@ -176,7 +178,7 @@ protected void doGet(final HttpServletRequest req, - - private void doGetInternal(final HttpServletRequest req, final HttpServletResponse resp) - throws ServletException, IOException, JSONException { -- switch (StringUtils.countMatches(req.getRequestURI(), "/")) { -+ switch (StringUtils.countMatches(getUri(req), "/")) { - case 1: - handleWelcomeReq(req, resp); - return; -@@ -211,9 +213,9 @@ protected void doPost(final HttpServletRequest req, - - private void doPostInternal(final HttpServletRequest req, final HttpServletResponse resp) - throws IOException, JSONException { -- switch (StringUtils.countMatches(req.getRequestURI(), "/")) { -+ switch (StringUtils.countMatches(getUri(req), "/")) { - case 3: -- if (req.getPathInfo().endsWith("/_cleanup")) { -+ if (req.getRequestURI().endsWith("/_cleanup")) { - cleanup(req, resp); - return; - } -@@ -235,4 +237,5 @@ private void doPostInternal(final HttpServletRequest req, final HttpServletRespo - ServletUtils.sendJsonError(req, resp, 400, "bad_request"); - } - -+ - } -diff --git a/src/main/java/com/github/rnewson/couchdb/lucene/PathParts.java b/src/main/java/com/github/rnewson/couchdb/lucene/PathParts.java -index d73222d..b4bd898 100644 ---- a/src/main/java/com/github/rnewson/couchdb/lucene/PathParts.java -+++ b/src/main/java/com/github/rnewson/couchdb/lucene/PathParts.java -@@ -20,6 +20,8 @@ - import java.util.regex.Matcher; - import java.util.regex.Pattern; - -+import static com.github.rnewson.couchdb.lucene.util.ServletUtils.getUri; -+ - public class PathParts { - - private static final Pattern QUERY_REGEX = Pattern -@@ -31,7 +33,7 @@ - private Matcher matcher; - - public PathParts(final HttpServletRequest req) { -- this(req.getRequestURI()); -+ this(getUri(req)); - } - - public PathParts(final String path) { -diff --git a/src/main/java/com/github/rnewson/couchdb/lucene/util/ServletUtils.java b/src/main/java/com/github/rnewson/couchdb/lucene/util/ServletUtils.java -index de0da0f..2c57695 100644 ---- a/src/main/java/com/github/rnewson/couchdb/lucene/util/ServletUtils.java -+++ b/src/main/java/com/github/rnewson/couchdb/lucene/util/ServletUtils.java -@@ -102,4 +102,10 @@ public static void sendJsonSuccess(final HttpServletRequest req, final HttpServl - } - } - -+ //Strip of context part of URI -+ public static String getUri(HttpServletRequest request) { -+ //Strip of context path if present -+ return request.getRequestURI().substring(request.getContextPath().length()); -+ } -+ - } diff --git a/third-party/couchdb-lucene/.travis.yml b/third-party/couchdb-lucene/.travis.yml deleted file mode 100644 index cc9f635f02..0000000000 --- a/third-party/couchdb-lucene/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: java -jdk: - - openjdk10 - - openjdk11 -cache: - directories: - - $HOME/.m2 diff --git a/third-party/couchdb-lucene/BREAKING_CHANGES b/third-party/couchdb-lucene/BREAKING_CHANGES deleted file mode 100644 index f2876155f3..0000000000 --- a/third-party/couchdb-lucene/BREAKING_CHANGES +++ /dev/null @@ -1,11 +0,0 @@ -2.0.0 - -* You must rebuild all indexes -* Removal of porter and snowball analyzers -* BYTE and SHORT removed from sort -* STRING maps to StringField, untokenized -* TEXT maps to TextField, tokenized - -0.5.1, 0.6.x - -Multiple queries must now be comma-separated (previously the document said "multiple q parameters"). This is due to a JSON encoding problem (a JSON object cannot have non-unique keys). diff --git a/third-party/couchdb-lucene/CONFIGURING_ANALYZERS.md b/third-party/couchdb-lucene/CONFIGURING_ANALYZERS.md deleted file mode 100644 index c34a80752d..0000000000 --- a/third-party/couchdb-lucene/CONFIGURING_ANALYZERS.md +++ /dev/null @@ -1,134 +0,0 @@ -# Configuring Analyzers - -There are many other analyzers included in Lucene and there are occasions where custom analyzers are needed. -There is now support for configuring additional analyzers without needing to further modify couchdb-lucene. -This extension is a loose adaptation of the [eXist-db/Lucene integration](https://github.com/eXist-db/exist/blob/develop/extensions/indexes/lucene/src/org/exist/indexing/lucene/AnalyzerConfig.java) - -The basic approach is to refer to the analyzer by its java class name, e.g., `org.apache.lucene.analysis.ar.ArabicAnalyzer` or `io.bdrc.lucene.bo.TibetanAnalyzer` -and specify any parameters that need to be supplied to the analyzer constructor, such as stop words or exclusion words lists and the like. - -Recall that the current analyzer configuration specifies a couchdb-lucene specific name and optional parameters as in: -``` json -"analyzer":"simple" -``` -or - -```json -"analyzer":"snowball:English" -``` -The analyzer configuration extension uses a json object to specify the analyzer java class and the parameters needed for the analyzer constructor: - -```json -"analyzer": - { "class": "org.apache.lucene.analysis.ar.ArabicAnalyzer", - "params": [ - { "name":"stopwords", - "type":"set", - "value": [ "آب", "أربع", "ألا" ] }, - { "name": "stemExclusionSet", - "type":"set", - "value": [ "قتول", "قتل" ] } ] - } -``` - -The parameter `name` field is optional and is not used other than to provide documentation. The `type` field is one of: - -- `string` -- `set` -- `int` -- `boolean` -- `file` - -The `set` type corresponds to Lucene `org.apache.lucene.analysis.CharArraySet` which is used in many analyzers to supply sets of stopwords and the like. - -The `file` type corresponds to `java.io.FileReader` and is used in some Analyzers to supply initialization information that is too cumbersome to be supplied directly in parameters. It is the responsibility of the Analyzer to close the file to prevent resource leaks. - -If the `type` field is not present it defaults to `string`. - -Most analyzers provide a nullary constructor so a minimal analyzer configuration is like: - -```json -"analyzer": { "class": "org.apache.lucene.analysis.hy.ArmenianAnalyzer" } -``` - -or - -```json -"analyzer": { "class": "org.apache.lucene.analysis.nl.DutchAnalyzer", - "params": [] } -``` - -The `SmartChineseAnalyzer` can be configured as follows: - -```json -"analyzer": - { "class": "org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer", - "params": [ - { "name": "useDefaultStopWords", - "type": "boolean", - "value": true } ] - } -``` - -All of the examples so far refer to analyzers that are found in the Lucene distribution that is included with couchdb-lucene. All that is needed to add custom analyzers is to put the analyzer class and any dependencies (packaged in a jar typically) on the classpath of couchdb-lucene. A simple way of doing this is to put a jar with the custom analyzers in the `./lib` directory of the couchdb-lucene install. For example, suppose that there is a custom analyzer, `io.bdrc.lucene.bo.TibetanAnalyzer` with associated filters and tokenizer in a jar `io.bdrc.lucene.bo.Tibetan.jar`, then putting the jar in `$COUCHDB-LUCENE_HOME/lib/` will ensure that the TibetanAnalyzer is on the classpath for couchdb-lucene. Then the following could be used to configure couchdb-lucene to use the TibetanAnalyzer: - -```json -"analyzer": - { "class": "io.bdrc.lucene.bo.TibetanAnalyzer", - "params": [ - { "name": "headWords", - "type": "file", - "value": "/usr/local/analyzers/bo/headWords.txt" } ] - } -``` - -The set of possible parameter types is not exhaustive, but for analyzers that have constructor parameters not included among the types available (e.g., `org.apache.lucene.analysis.ja.JapaneseAnalyzer`), it is sufficient to build a wrapper analyzer that retrieves information from a file and builds the necessary parameters and then calls the appropriate constructor. - -The analyzer configuration extension may be used in the `perfield` and `ngram` analyzer configurations as well as with the `analyzer` parameter in a search request: - -```json -"analyzer": - "perfield:{ - default:\"keyword\", - lang_bo:{\"class\":\"io.bdrc.lucene.bo.TibetanAnalyzer\"}, - lang_sa:{\"class\":\"org.apache.lucene.analysis.hi.HindiAnalyzer\"} - }" -``` - -The above illustrates that extension configurations can be mixed with the current couchdb-lucene configurations. - -The extension also permits using json notation in place of the current analyzer syntax: - -```json -"analyzer": - { "perfield": - {"default": "keyword", - "lang_bo": {"class": "io.bdrc.lucene.bo.TibetanAnalyzer"}, - "lang_sa": {"class": "org.apache.lucene.analysis.hi.HindiAnalyzer"} - } - } -``` - -or - -```json -"analyzer": { "german": {} } -``` - -or - -```json -"analyzer": { "cjk": "" } -``` - -or - -```json -"analyzer": { "snowball": "English" } -``` - -or - -```json -"analyzer": { "ngram": { "analyzer": "simple", "min": 2, "max": 3 } } -``` \ No newline at end of file diff --git a/third-party/couchdb-lucene/LICENSE b/third-party/couchdb-lucene/LICENSE deleted file mode 100644 index d9a10c0d8e..0000000000 --- a/third-party/couchdb-lucene/LICENSE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS diff --git a/third-party/couchdb-lucene/NEWS b/third-party/couchdb-lucene/NEWS deleted file mode 100644 index 23aff0a761..0000000000 --- a/third-party/couchdb-lucene/NEWS +++ /dev/null @@ -1,107 +0,0 @@ -Version 1.0.1 --------------------------- - -* Use Lucene 4.9.0 index format -* Honor "index" property when adding strings - -Version 1.0.0 ---------------------------- - -* Upgrade to Lucene 4.9.0 -* Upgrade to Tika 1.5 -* Use the full OOXML Schemas from Apache POI, to make Tika - able to parse Office documents that use exotic features -* Allow search by POST (using form data) - -Version 0.9.0 - -* Upgrade to Rhino 1.7R4 -* Upgrade to Lucene 3.6.1 -* Upgrade to Tika 1.2 -* Add JSON.stringify support (see above) -* Support highlights - -Version 0.8.0 -------------- - -* Upgrade to Lucene 3.5 -* Upgrade to Tika 1.0 -* Add include_fields=[field] to allow control over stored fields -* Add [lucene] allowLeadingWildcard=true|false option -* Add [lucene] lowercaseExpandedTerms=true|false -* Include uuid and digest in view info -* Include ddoc and view name in compilation error log -* Fix hang when last change was _revs_limit or _security -* Add "classic" analyzer for pre-3.3 analysis - -Version 0.7.0 -------------- - -* Upgrade to Lucene 3.3 -* Upgrade to Tika 0.9 -* Add snowball analyzer -* Add BigCouch compatibility -* Return stored numeric fields as numbers, not strings -* include_docs=true now gets docs once, not twice - -Version 0.6.0 -------------- - -* Upgrade to Lucene 3.0.3. -* Upgrade to Tika 0.8. -* Changed JSON library for improved JSON parsing. -* Control over term vectors when indexing. -* Added the whitespace analyzer. -* Include init.d startup script in zip. -* Allow query-time override of analyzer. -* Split multiple queries with embedding commas correctly. -* Support for remote couchdb nodes over HTTPS. - -Version 0.5.6 -------------- - -* Full text functions that do not compile caused clients to hang forever. -* Basic authentication backported from master for require_valid_user=true scenario. - -Version 0.5.5 -------------- - -* Pass query string through analyzer so that the same stemming rules are applied as when indexing. - -Version 0.5.4 -------------- - -* Allow searching on numeric terms. -* Fix reindexing bug when index function changes. -* Enhance the 'welcome' message to include the correct version string. - -Version 0.5.3 -------------- - -* Permit url-escaped / character in database names. - -Version 0.5.2 -------------- - -* Make searching thread-safe. - - -Version 0.5.1 -------------- - -* Support multiple queries in a single call through the Python script. -* Allow customization of field boost. -* Improved handled for emitted null values. -* Improved date handling. - - -Version 0.5 ------------ - -* c-l now runs as a standalone server. -* Compatibility with CouchDB 0.10.0, 0.10.1 and 0.11. -* Upgraded to use Lucene 3.0.1. -* Upgraded to use Tika 0.6. -* Efficient numeric range queries. -* Near-realtime indexing updates. -* Added ability to set multiple q parameters to perform several queries in one call. diff --git a/third-party/couchdb-lucene/README.md b/third-party/couchdb-lucene/README.md deleted file mode 100644 index 27bcfcfd1a..0000000000 --- a/third-party/couchdb-lucene/README.md +++ /dev/null @@ -1,681 +0,0 @@ -# couchdb-lucene [![Build Status](https://secure.travis-ci.org/rnewson/couchdb-lucene.svg)](http://travis-ci.org/rnewson/couchdb-lucene) - -## Version Compatibility -CouchDB-Lucene works with all version of CouchDB from 0.10 upwards. - -## Issue Tracking -Issue tracking at [github](http://github.com/rnewson/couchdb-lucene/issues). - -## Minimum System Requirements -Java 1.8 is required; Oracle Java 8 or OpenJDK 8 are recommended. - -## Build and run couchdb-lucene -If you are on OS X, you might find it easiest to: - -```bash -brew install couchdb-lucene -``` - -1. Install Maven (2 or 3). -2. checkout repository -3. type 'mvn' -4. cd target -5. unzip couchdb-lucene-\.zip -6. cd couchdb-lucene-\ -7. ./bin/run - -The zip file contains all the couchdb-lucene code, dependencies, startup scripts and configuration files you need, so unzip it wherever you wish to install couchdb-lucene. - -If you want to run couchdb-lucene on a servlet container like Tomcat, you can build the war file using Maven - -```bash -mvn war:war -``` - -## Configure CouchDB -The following settings are needed in CouchDB's local.ini file in order for it to communicate with couchdb-lucene: - -### Proxy handler (for CouchDB versions from 1.1 onward) -```ini -[httpd_global_handlers] -_fti = {couch_httpd_proxy, handle_proxy_req, <<"http://127.0.0.1:5985">>} -``` - -### Python hook script (for CouchDB versions prior to 1.1) -```ini -[couchdb] -os_process_timeout=60000 ; increase the timeout from 5 seconds. - -[external] -fti=/path/to/python /path/to/couchdb-lucene/tools/couchdb-external-hook.py - -[httpd_db_handlers] -_fti = {couch_httpd_external, handle_external_req, <<"fti">>} -``` - -#### Hook options -You can pass options to the python script like so: -```ini -[external] -fti=/path/to/python "/path/to/couchdb-lucene/tools/couchdb-external-hook.py --option-name value" -``` - -|Option |Meaning |Default Value| -|-------------|----------------------------------------------------------------------------|-------------| -|--remote-host|The hostname of the couchdb-lucene server |localhost | -|--remote-port|The port of the couchdb-lucene server |5985 | -|--local-key |The key for the local couchdb instance as known to the couchdb-lucene server|local | - -## Configure couchdb-lucene -couchdb-lucene runs in a single, standalone JVM. As such, you can choose to locate your couchdb-lucene server on a different machine to couchdb if you wish, or keep it on the same machine, it's your call. - -## Start couchdb-lucene -To start couchdb-lucene, run: -```bash -bin/run -``` - -To stop couchdb-lucene, simply kill the Java process. - -## Indexing Strategy -### Document Indexing -You must supply a index function in order to enable couchdb-lucene as, by default, nothing will be indexed. To suppress a document from the index, return null. It's more typical to return a single Document object which contains everything you'd like to query and retrieve. You may also return an array of Document objects if you wish. - -You may add any number of index views in any number of design documents. All searches will be constrained to documents emitted by the index functions. - -Here's an complete example of a design document with couchdb-lucene features: - -```json -{ - "_id":"_design/foo", - "fulltext": { - "by_subject": { - "index":"function(doc) { var ret=new Document(); ret.add(doc.subject); return ret }" - }, - "by_content": { - "index":"function(doc) { var ret=new Document(); ret.add(doc.content); return ret }" - } - } -} -``` - -Here are some example URL's for the given design document: - -### Using the Python hook script -``` -http://localhost:5984/database/_fti/_design/foo/by_subject?q=hello -http://localhost:5984/database/_fti/_design/foo/by_content?q=hello -``` - -### Using the proxy handler -``` -http://localhost:5984/_fti/local/database/_design/foo/by_subject?q=hello -http://localhost:5984/_fti/local/database/_design/foo/by_content?q=hello -``` - -A fulltext object contains multiple index view declarations. An index view consists of: - -***analyzer*** - (optional) The analyzer to use - -***defaults*** - (optional) The default for numerous indexing options can be overridden here. A full list of options follows. - -***index*** - The indexing function itself, documented below. - -#### The Defaults Object -The following indexing options can be defaulted: - -|name|description|available options|default| -|----|-----------|-----------------|-------| -|field|the field name to index under|user-defined|default| -|type|the type of the field|date, double, float, int, long, string, text|text| -|store|whether the data is stored. The value will be returned in the search result.|yes, no|no| -|boost|Sets the boost factor hits on this field. This value will be multiplied into the score of all hits on this this field of this document.|floating-point value|1.0| - -#### String vs Text - -There are two supported types that sound equivalent, *string* and *text*, but they are very different. A text field will be tokenized into words and is usually what you expect from a full-text index. A string field is not tokenized, only exact matching will work. The advantage to string fields is that they have a meaningful sort order. - -#### The Analyzer Option - -Lucene has numerous ways of converting free-form text into tokens, these classes are called Analyzer's. By default, the StandardAnalyzer is used which lower-cases all text, drops common English words ("the", "and", and so on), among other things. This processing might not always suit you, so you can choose from several others by setting the "analyzer" field to one of the following values: - -- brazilian -- chinese -- cjk -- czech -- dutch -- english -- french -- german -- keyword -- perfield -- porter -- russian -- simple -- snowball -- standard -- thai -- whitespace -- ngram - -##### The Snowball Analyzer -This analyzer requires an extra argument to specify the language (see [here](http://lucene.apache.org/java/3_0_3/api/contrib-snowball/org/apache/lucene/analysis/snowball/SnowballAnalyzer.html) for details): - -```json -"analyzer":"snowball:English" -``` - -Note: the argument is case-sensitive and is passed directly to the `SnowballAnalyzer`'s constructor. - -##### The Per-field Analyzer -The "perfield" option lets you use a different analyzer for different fields and is configured as follows: - -```json -"analyzer":"perfield:{field_name:\"analyzer_name\"}" -``` - -Unless overridden, any field name not specified will be handled by the standard analyzer. To change the default, use the special default field name: - -```json -"analyzer":"perfield:{default:\"keyword\"}" -``` - -##### The Ngram Analyzer -The "ngram" analyzer lets you break down the output of any other analyzer into ngrams ("foo" becomes "fo" and "oo"). - -```json -"analyzer":"ngram:{analyzer:\"simple\",min:2,max:3}" -``` - -If not specified, the delegated analyzer is "standard" and min and max ngram sizes are 1 and 2 respectively. - -##### Configuring additional analyzers - -There are many other analyzers included in Lucene and there are also occasions where custom analyzers not included in Lucene are needed. -There is now support for [configuring additional analyzers](CONFIGURING_ANALYZERS.md) without needing to further modify couchdb-lucene. - -#### The Document class -You may construct a new Document instance with: - -```js -var doc = new Document(); -``` - -Data may be added to this document with the add method which takes an optional second object argument that can override any of the above default values. - -```js -// Add with all the defaults. -doc.add("value"); - -// Add a numeric field. -doc.add(35, {"type":"int"}); - -// Add a date field. -doc.add(new Date("1972/1/6 16:05:00"), {"type":"date"}); -doc.add(new Date("January 6, 1972 16:05:00"), {"type":"date"}); - -// Add a date field (object must be a Date object - -// Add a subject field. -doc.add("this is the subject line.", {"field":"subject"}); - -// Add but ensure it's stored. -doc.add("value", {"store":"yes"}); - -// Add but don't analyze. -doc.add("don't analyze me", {"index":"not_analyzed"}); - -// Extract text from the named attachment and index it to a named field -doc.attachment("attachment field", "attachment name"); - -// log an event (trace, debug, info, warn and error are available) -if (doc.foo) { - log.info("doc has foo property!"); -} -``` - -#### Example Index Functions -##### Index Everything -```js -function(doc) { - var ret = new Document(); - - function idx(obj) { - for (var key in obj) { - switch (typeof obj[key]) { - case 'object': - idx(obj[key]); - break; - case 'function': - break; - default: - ret.add(obj[key]); - break; - } - } - }; - - idx(doc); - - if (doc._attachments) { - for (var i in doc._attachments) { - ret.attachment("default", i); - } - } - - return ret; -} -``` - -##### Index Nothing -```js -function(doc) { - return null; -} -``` - -##### Index Select Fields -```js -function(doc) { - var result = new Document(); - result.add(doc.subject, {"field":"subject", "store":"yes"}); - result.add(doc.content, {"field":"subject"}); - result.add(new Date(), {"field":"indexed_at"}); - return result; -} -``` - -##### Index Attachments -```js -function(doc) { - var result = new Document(); - for(var a in doc._attachments) { - result.attachment("default", a); - } - return result; -} -``` - -##### A More Complex Example -```js -function(doc) { - var mk = function(name, value, group) { - var ret = new Document(); - ret.add(value, {"field": group, "store":"yes"}); - ret.add(group, {"field":"group", "store":"yes"}); - return ret; - }; - if(doc.type != "reference") return null; - var ret = new Array(); - for(var g in doc.groups) { - ret.push(mk("library", doc.groups[g].library, g)); - ret.push(mk("method", doc.groups[g].method, g)); - ret.push(mk("target", doc.groups[g].target, g)); - } - return ret; -} -``` - -### Attachment Indexing -Couchdb-lucene uses [Apache Tika](http://lucene.apache.org/tika/) to index attachments of the following types, assuming the correct content_type is set in couchdb; - -#### Supported Formats -- Excel spreadsheets (application/vnd.ms-excel) -- HTML (text/html) -- Images (image/*) -- Java class files -- Java jar archives -- MP3 (audio/mp3) -- OpenDocument (application/vnd.oasis.opendocument.*) -- Outlook (application/vnd.ms-outlook) -- PDF (application/pdf) -- Plain text (text/plain) -- Powerpoint presentations (application/vnd.ms-powerpoint) -- RTF (application/rtf) -- Visio (application/vnd.visio) -- Word documents (application/msword) -- XML (application/xml) - -## Searching with couchdb-lucene -You can perform all types of queries using Lucene's default [query syntax](http://lucene.apache.org/java/3_6_2/queryparsersyntax.html). - -### Numeric range queries -In addition to normal text-based range searches (using the "field:[lower TO upper]" syntax), couchdb-lucene also supports numeric range searches for the following types: int, long, float, double and date. The type is specified after the field name, as follows: - -|type|example| -|----|-------| -|int|field:[0 TO 100]| -|long|field:[0 TO 100]| -|float|field:[0.0 TO 100.0]| -|double|field:[0.0 TO 100.0]| -|date|field:[from TO to] where from and to match any of these patterns: `"yyyy-MM-dd'T'HH:mm:ssZ"`, `"yyyy-MM-dd'T'HH:mm:ss"`, `"yyyy-MM-ddZ"`, `"yyyy-MM-dd"`, `"yyyy-MM-dd'T'HH:mm:ss.SSSZ"`, `"yyyy-MM-dd'T'HH:mm:ss.SSS"`. So, in order to search for articles published in July, you would issue a following query: `published_at:["2010-07-01T00:00:00"+TO+"2010-07-31T23:59:59"]`| - -An example numeric range query for spatial searching. - -``` -?q=pizza AND lat:[51.4707 TO 51.5224] AND long:[-0.6622 TO -0.5775] -``` - -### Numeric term queries -Fields indexed with numeric types can still be queried as normal terms, couchdb-lucene just needs to know the type. For example, `?q=age:12` will find all documents where the field called 'age' has a value of 12 (when the field was indexed as "type":"int". - -### Search methods -You may use HTTP GET or POST. For POST, use application/x-www-form-urlencoded format. - -### Search parameters -The following parameters can be passed for more sophisticated searches: - -***analyzer*** - Override the default analyzer used to parse the q parameter - -***callback*** - Specify a JSONP callback wrapper. The full JSON result will be prepended with this parameter and also placed with parentheses." - -***debug*** - Setting this to true disables response caching (the query is executed every time) and indents the JSON response for readability. - -***default_operator*** - Change the default operator for boolean queries. Defaults to "OR", other permitted value is "AND". - -***force_json*** - Usually couchdb-lucene determines the Content-Type of its response based on the presence of the Accept header. If Accept contains "application/json", you get "application/json" in the response, otherwise you get "text/plain;charset=utf8". Some tools, like JSONView for FireFox, do not send the Accept header but do render "application/json" responses if received. Setting force_json=true forces all response to "application/json" regardless of the Accept header. - -***include_docs*** - whether to include the source docs - -***include_fields*** - By default, *all* stored fields are returned with results. Use a comma-separate list of field names with this parameter to refine the response - -***highlights*** - Number of highlights to include with results. Default is *0*. This uses the *fast-vector-highlighter* plugin. - -***highlight_length*** - Number of characters to include in a highlight row. Default and minimum is *18*. - -***limit*** - the maximum number of results to return. Default is *25*. - -***q*** - the query to run (e.g, subject:hello). If not specified, the default field is searched. Multiple queries can be supplied, separated by commas; the resulting JSON will be an array of responses. - -***skip*** - the number of results to skip - -***sort*** - the comma-separated fields to sort on. Prefix with / for ascending order and \ for descending order (ascending is the default if not specified). Type-specific sorting is also available by appending the type between angle brackets (e.g, sort=amount). Supported types are 'float', 'double', 'int', 'long' and 'date'. - -***stale=ok*** - If you set the *stale* option to *ok*, couchdb-lucene will not block if the index is not up to date and it will immediately return results. Therefore searches may be faster as Lucene caches important data (especially for sorting). A query without stale=ok will block and use the latest data committed to the index. Unlike CouchDBs stale=ok option for views, couchdb-lucene will trigger an index update unless one is already running. - -*All parameters except 'q' are optional.* - -### Special Fields -***_id*** - The _id of the document. - -### Dublin Core -All Dublin Core attributes are indexed and stored if detected in the attachment. Descriptions of the fields come from the Tika javadocs. - - -***_dc.contributor*** - An entity responsible for making contributions to the content of the resource. - -***_dc.coverage*** - The extent or scope of the content of the resource. - -***_dc.creator*** - An entity primarily responsible for making the content of the resource. - -***_dc.date*** - A date associated with an event in the life cycle of the resource. - -***_dc.description*** - An account of the content of the resource. - -***_dc.format*** - Typically, Format may include the media-type or dimensions of the resource. - -***_dc.identifier*** - Recommended best practice is to identify the resource by means of a string or number conforming to a formal identification system. - -***_dc.language*** - A language of the intellectual content of the resource. - -***_dc.modified*** - Date on which the resource was changed. - -***_dc.publisher*** - An entity responsible for making the resource available. - -***_dc.relation*** - A reference to a related resource. - -***_dc.rights*** - Information about rights held in and over the resource. - -***_dc.source*** - A reference to a resource from which the present resource is derived. - -***_dc.subject*** - The topic of the content of the resource. - -***_dc.title*** - A name given to the resource. - -***_dc.type*** - The nature or genre of the content of the resource. - -### Examples - -### Using the Python hook script -``` -http://localhost:5984/dbname/_fti/_design/foo/view_name?q=field_name:value -http://localhost:5984/dbname/_fti/_design/foo/view_name?q=field_name:value&sort=other_field -http://localhost:5984/dbname/_fti/_design/foo/view_name?debug=true&sort=billing_size&q=body:document AND customer:[A TO C] -``` - -### Using the proxy handler -``` -http://localhost:5984/_fti/local/dbname/_design/foo/view_name?q=field_name:value -http://localhost:5984/_fti/local/dbname/_design/foo/view_name?q=field_name:value&sort=other_field -http://localhost:5984/_fti/local/dbname/_design/foo/view_name?debug=true&sort=billing_size&q=body:document AND customer:[A TO C] -``` - -### Search Results Format -The search result contains a number of fields at the top level, in addition to your search results. - -***etag*** - An opaque token that reflects the current version of the index. This value is also returned in an ETag header to facilitate HTTP caching. - -***fetch_duration*** - The number of milliseconds spent retrieving the documents. - -***limit*** - The maximum number of results that can appear. - -***q*** - The query that was executed. - -***rows*** - The search results array, described below. - -***search_duration*** - The number of milliseconds spent performing the search. - -***skip*** - The number of initial matches that was skipped. - -***total_rows*** - The total number of matches for this query. - -### The search results array - -The search results arrays consists of zero, one or more objects with the following fields: - -***doc*** - The original document from couch, if requested with include_docs=true - -***fields*** - All the fields that were stored with this match - -***id*** - The unique identifier for this match. - -***score*** - The normalized score (0.0-1.0, inclusive) for this match - -Here's an example of a JSON response without sorting: - -```json -{ - "q": "+content:enron", - "skip": 0, - "limit": 2, - "total_rows": 176852, - "search_duration": 518, - "fetch_duration": 4, - "rows": [ - { - "id": "hain-m-all_documents-257.", - "score": 1.601625680923462 - }, - { - "id": "hain-m-notes_inbox-257.", - "score": 1.601625680923462 - } - ] -} -``` - -And the same with sorting: - -```json -{ - "q": "+content:enron", - "skip": 0, - "limit": 3, - "total_rows": 176852, - "search_duration": 660, - "fetch_duration": 4, - "sort_order": [ - { - "field": "source", - "reverse": false, - "type": "string" - }, - { - "reverse": false, - "type": "doc" - } - ], - "rows": [ - { - "id": "shankman-j-inbox-105.", - "score": 0.6131107211112976, - "sort_order": [ - "enron", - 6 - ] - }, - { - "id": "shankman-j-inbox-8.", - "score": 0.7492915391921997, - "sort_order": [ - "enron", - 7 - ] - }, - { - "id": "shankman-j-inbox-30.", - "score": 0.507369875907898, - "sort_order": [ - "enron", - 8 - ] - } - ] -} -``` - -#### Content-Type of response -The Content-Type of the response is negotiated via the Accept request header like CouchDB itself. If the Accept header includes "application/json" then that is also the Content-Type of the response. If not, "text/plain;charset=utf-8" is used. - -## Fetching information about the index -Calling couchdb-lucene without arguments returns a JSON object with information about the index. - -``` -http://127.0.0.1:5984//_fti/_design/foo/ -``` - -returns: - -```json -{"current":true,"disk_size":110674,"doc_count":397,"doc_del_count":0, -"fields":["default","number"],"last_modified":"1263066382000", -"optimized":true,"ref_count":2} -``` - -## Index Maintenance -For optimal query speed you can optimize your indexes. This causes the index to be rewritten into a single segment. - -```bash -curl -X POST http://localhost:5984//_fti/_design/foo//_optimize -``` - -If you just want to expunge pending deletes, then call: - -```bash -curl -X POST http://localhost:5984//_fti/_design/foo//_expunge -``` - -If you recreate databases or frequently change your fulltext functions, you will probably have old indexes lying around on disk. To remove all of them, call: - -```bash -curl -X POST http://localhost:5984//_fti/_cleanup -``` - -## Authentication - -By default couchdb-lucene does not attempt to authenticate to CouchDB. If you have set CouchDB's require_valid_user to true, you will need to modify couchdb-lucene.ini. Change the url setting to include a valid username and password. e.g, the default setting is: - -```ini -[local] -url=http://localhost:5984/ -``` - -Change it to: - -```ini -[local] -url=http://foo:bar@localhost:5984/ -``` - -and couchdb-lucene will authenticate to couchdb. - -## Other Tricks -A couple of 'expert' options can be set in the couchdb-lucene.ini file; - -Leading wildcards are prohibited by default as they perform very poorly most of the time. You can enable them as follows: - -```ini -[lucene] -allowLeadingWildcard=true -``` - -Lucene automatically converts terms to lower case in wildcard situations. You can disable this with: - -```ini -[lucene] -lowercaseExpandedTerms=false -``` - -CouchDB-Lucene will keep your indexes up to date automatically but this consumes resources (network sockets). You can ask CouchDB-Lucene to stop updating an index after a timeout with: - -```ini -[lucene] -changes_timeout = 60000 -``` - diff --git a/third-party/couchdb-lucene/THANKS.md b/third-party/couchdb-lucene/THANKS.md deleted file mode 100644 index d528ab468b..0000000000 --- a/third-party/couchdb-lucene/THANKS.md +++ /dev/null @@ -1,5 +0,0 @@ -* Thanks to Paul Davis for contributing the enhanced Javascript indexing API. -* Thanks to Adam Lofts for the performance boosting JSONDocumentAdapter et al. -* Thanks to Santiago M. Mola for the termvector option. -* Thanks to Joe Hillenbrand for adding default result limit to config. -* Thanks to Nate Steffen for adding highlighting. diff --git a/third-party/couchdb-lucene/TODO b/third-party/couchdb-lucene/TODO deleted file mode 100644 index 2c85fb7ff9..0000000000 --- a/third-party/couchdb-lucene/TODO +++ /dev/null @@ -1,22 +0,0 @@ -0.6 - -OpenJDK compatibility! - -* Authentication to secured couchdb. --done -* add X-CouchDB-WWW-Authenticate=Basic realm="couchdb-lucene" and disable pre-emptive auth! - -* make attachment call support "store":"yes|no" - -* configure osxappbundle:bundle to make one-click OSX install. - -* distributed search. - -UNSCHEDULED - -* LuceneDictionary/spellcheck/suggestions mode. -* _show and _list-like support for search results. -* hit highlighting (lucene contrib). -* clustering (carrot2). -* JSONQuery? (http://docs.persvr.org/documentation/jsonquery) -* geospatial (need TrieRangeQuery from 2.9) - diff --git a/third-party/couchdb-lucene/couchdb-external-hook.py b/third-party/couchdb-lucene/couchdb-external-hook.py deleted file mode 100755 index 5f085d1e47..0000000000 --- a/third-party/couchdb-lucene/couchdb-external-hook.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/python - -import httplib -import optparse as op -import sys -import traceback -import urllib - -try: - import json -except: - import simplejson as json - -__usage__ = "%prog [OPTIONS]" - -httpdict = {"etag": "ETag", "content-type": "Content-Type"} - - -def options(): - return [ - op.make_option('--remote-host', dest='remote_host', - default="localhost", - help="Hostname of the couchdb-lucene server. [%default]"), - op.make_option('--remote-port', dest='remote_port', type='int', - default=5985, - help="Port of the couchdb-lucene server. [%default]"), - op.make_option('--local-key', dest='key', - default="local", - help="Configured key name for this couchdb instance. [%default]"), - ] - - -def main(): - parser = op.OptionParser(usage=__usage__, option_list=options()) - opts, args = parser.parse_args() - - if len(args): - parser.error("Unrecognized arguments: %s" % ' '.join(args)) - for req in requests(): - res = httplib.HTTPConnection(opts.remote_host, opts.remote_port) - try: - resp = respond(res, req, opts.key) - except Exception, e: - body = traceback.format_exc() - resp = mkresp(500, body, {"Content-Type": "text/plain"}) - - res.close() - sys.stdout.write(json.dumps(resp)) - sys.stdout.write("\n") - sys.stdout.flush() - - -def requests(): - line = sys.stdin.readline() - while line: - yield json.loads(line) - line = sys.stdin.readline() - - -def respond(res, req, key): - path = req.get("path", []) - - # Drop name of external hook. - del path[1] - - # URL-escape each part - for index, item in enumerate(path): - path[index] = urllib.quote(path[index], safe="") - - path = '/'.join(['', key] + path) - params = urllib.urlencode( - dict([k, v.encode('utf-8')] for k, v in req["query"].items())) - path = '?'.join([path, params]) - - req_headers = {} - for h in req.get("headers", []): - if h.lower() in ["accept", "if-none-match"]: - req_headers[h] = req["headers"][h] - - # verb renamed to method in 0.11 onwards. - if "method" in req: - method = req["method"] - else: - method = req["verb"] - - res.request(method, path, headers=req_headers) - resp = res.getresponse() - - resp_headers = {} - for h, v in resp.getheaders(): - if h.lower() in httpdict: - resp_headers[httpdict[h]] = resp.getheader(h, []) - - return mkresp(resp.status, resp.read(), resp_headers) - - -def mkresp(code, body, headers=None): - ret = {"code": code, "body": body} - if headers is not None: - ret["headers"] = headers - return ret - - -if __name__ == "__main__": - main() - diff --git a/third-party/couchdb-lucene/pom.xml b/third-party/couchdb-lucene/pom.xml deleted file mode 100644 index 6c1739f009..0000000000 --- a/third-party/couchdb-lucene/pom.xml +++ /dev/null @@ -1,282 +0,0 @@ - - - - 4.0.0 - com.github.rnewson.couchdb.lucene - couchdb-lucene - CouchDB Lucene - Full-text indexing for CouchDB - http://github.com/rnewson/couchdb-lucene/ - 2.2.0-SNAPSHOT - war - - org.eclipse.sw360 - third-party - 18.99.1 - - - UTF-8 - UTF-8 - ${backend.deploy.dir} - true - - - - org.apache.httpcomponents - httpclient - - - org.apache.httpcomponents - httpcore - - - net.jcip - jcip-annotations - ${jcip-annotations.version} - compile - - - org.eclipse.jetty - jetty-server - ${jetty.version} - - - org.eclipse.jetty - jetty-servlet - ${jetty.version} - - - org.eclipse.jetty - jetty-servlets - ${jetty.version} - - - commons-io - commons-io - - - commons-configuration - commons-configuration - ${commons-configuration.version} - - - commons-codec - commons-codec - - - org.json - json - - - org.mozilla - rhino - 1.7R4 - - - org.apache.lucene - lucene-core - ${lucene.version} - - - org.apache.lucene - lucene-analyzers-common - ${lucene.version} - - - org.apache.lucene - lucene-analyzers-smartcn - ${lucene.version} - - - org.apache.lucene - lucene-queryparser - ${lucene.version} - - - org.apache.lucene - lucene-queries - ${lucene.version} - - - org.apache.tika - tika-core - ${tika.version} - - - org.apache.lucene - lucene-highlighter - ${lucene.version} - - - org.apache.tika - tika-parsers - ${tika.version} - - - org.apache.poi - poi-ooxml-schemas - ${poi.version} - - - org.erlang.otp - jinterface - 1.5.3.2 - - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.slf4j - slf4j-simple - test - - - org.hamcrest - hamcrest-all - 1.1 - test - - - junit - junit - test - - - org.apache.logging.log4j - log4j-core - test - - - javax.servlet - javax.servlet-api - provided - - - - github - http://github.com/rnewson/couchdb-lucene/issues - - - scm:git:git://github.com/rnewson/couchdb-lucene.git - - http://github.com/rnewson/couchdb-lucene/ - - - - Apache 2 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - - - - - rnewson - Robert Newson - robert.newson@gmail.com - - Developer - - 0 - - - - couchdb-lucene - assembly:assembly - - - org.codehaus.mojo - exec-maven-plugin - 1.2.1 - - - - java - - - - - com.github.rnewson.couchdb.lucene.Main - - - - maven-javadoc-plugin - ${maven-javadoc.version} - - 1.8 - false - - http://java.sun.com/javase/6/docs/api/ - - - - - maven-compiler-plugin - 2.0.2 - - 1.8 - 1.8 - UTF-8 - - - - org.apache.maven.plugins - maven-assembly-plugin - 3.6.0 - - - src/main/assembly/dist.xml - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.2 - - - - true - - true - - - - - - - org.apache.maven.plugins - maven-war-plugin - - @{artifactId}@.@{extension}@ - - - - - org.apache.maven.plugins - maven-eclipse-plugin - - true - true - - - - - diff --git a/third-party/couchdb-lucene/src/main/assembly/dist.xml b/third-party/couchdb-lucene/src/main/assembly/dist.xml deleted file mode 100644 index 5bd1d790b9..0000000000 --- a/third-party/couchdb-lucene/src/main/assembly/dist.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - dist - - zip - tar.gz - - - - - ${project.basedir}/README* - ${project.basedir}/LICENSE* - ${project.basedir}/NOTICE* - - / - - - - - ${project.basedir}/src/main/bin/run - 755 - /bin - - - ${project.basedir}/src/main/bin/run.bat - 755 - /bin - - - ${project.basedir}/src/main/bin/kill_ppid - 755 - /bin - - - ${project.basedir}/src/main/resources/couchdb-lucene.ini - - 644 - /conf - - - ${project.basedir}/couchdb-external-hook.py - 777 - /tools - - - - ${project.basedir}/src/main/tools/etc/init.d/couchdb-lucene - - 755 - /tools/etc/init.d/couchdb-lucene - - - - - runtime - /lib - - - diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/Config.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/Config.java deleted file mode 100644 index 758ff63fab..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/Config.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import org.apache.commons.configuration.ConfigurationException; -import org.apache.commons.configuration.HierarchicalINIConfiguration; -import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy; -import org.apache.http.client.HttpClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; - -public final class Config { - - private static final Logger LOG = LoggerFactory.getLogger(Config.class); - - private static final String CONFIG_FILE = "couchdb-lucene.ini"; - private static final String LUCENE_DIR = "lucene.dir"; - private static final String DEFAULT_DIR = "indexes"; - - private final HierarchicalINIConfiguration configuration; - - public Config() throws ConfigurationException { - this.configuration = new HierarchicalINIConfiguration(Config.class - .getClassLoader().getResource(CONFIG_FILE)); - this.configuration - .setReloadingStrategy(new FileChangedReloadingStrategy()); - } - - public final HierarchicalINIConfiguration getConfiguration() { - return this.configuration; - } - - public final File getDir() throws IOException { - final File dir = new File(this.configuration.getString(LUCENE_DIR, - DEFAULT_DIR)); - if (!dir.exists() && !dir.mkdir()) { - throw new IOException("Could not create " + dir.getCanonicalPath()); - } - if (!dir.canRead()) { - throw new IOException(dir + " is not readable."); - } - if (!dir.canWrite()) { - throw new IOException(dir + " is not writable."); - } - LOG.info("Index output goes to: " + dir.getCanonicalPath()); - return dir; - } - - public final HttpClient getClient() throws MalformedURLException { - HttpClientFactory.setIni(this.configuration); - return HttpClientFactory.getInstance(); - } -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/CustomQueryParser.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/CustomQueryParser.java deleted file mode 100644 index b7c1ecd448..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/CustomQueryParser.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import com.github.rnewson.couchdb.lucene.couchdb.FieldType; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.queryparser.classic.QueryParser; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Sort; -import org.apache.lucene.search.SortField; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Custom query parser that uses NumericFieldQuery where appropriate. - * - * @author rnewson - */ -public final class CustomQueryParser extends QueryParser { - - public CustomQueryParser(final String f, final Analyzer a) { - super(f, a); - } - - public static Sort toSort(final String sort) throws ParseException { - if (sort == null) { - return null; - } else { - final String[] split = sort.split(","); - final SortField[] sort_fields = new SortField[split.length]; - for (int i = 0; i < split.length; i++) { - String tmp = split[i]; - final boolean reverse = tmp.charAt(0) == '\\'; - // Strip sort order character. - if (tmp.charAt(0) == '\\' || tmp.charAt(0) == '/') { - tmp = tmp.substring(1); - } - final SortField sortField; - if ("_score".equals(tmp)) { - sortField = new SortField(null, SortField.Type.SCORE, reverse); - } else if ("_doc".equals(tmp)) { - sortField = new SortField(null, SortField.Type.DOC, reverse); - } else { - final TypedField typedField = new TypedField(tmp); - sortField = new SortField(typedField.getName(), typedField - .toSortField(), reverse); - } - sort_fields[i] = sortField; - } - return new Sort(sort_fields); - } - } - - public static JSONArray toJSON(final SortField[] sortFields) throws JSONException { - final JSONArray result = new JSONArray(); - for (final SortField field : sortFields) { - final JSONObject col = new JSONObject(); - col.put("field", field.getField()); - col.put("reverse", field.getReverse()); - - final String type; - switch (field.getType()) { - case DOC: - type = "doc"; - break; - case SCORE: - type = "score"; - break; - case INT: - type = "int"; - break; - case LONG: - type = "long"; - break; - case CUSTOM: - type = "custom"; - break; - case DOUBLE: - type = "double"; - break; - case FLOAT: - type = "float"; - break; - case STRING: - type = "string"; - break; - default: - type = "unknown"; - break; - } - col.put("type", type); - result.put(col); - } - return result; - } - - @Override - protected Query getRangeQuery(final String field, final String lower, final String upper, - final boolean lowerInclusive, final boolean upperInclusive) - throws ParseException { - return new TypedField(field).toRangeQuery(lower, upper, lowerInclusive, upperInclusive); - } - - @Override - protected Query getFieldQuery(final String field, final String queryText, final boolean quoted) - throws ParseException { - final TypedField typedField = new TypedField(field); - if (typedField.getType() == FieldType.TEXT) { - return super.getFieldQuery(field, queryText, quoted); - } - return typedField.toTermQuery(queryText); - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/DatabaseIndexer.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/DatabaseIndexer.java deleted file mode 100644 index 89bb870a17..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/DatabaseIndexer.java +++ /dev/null @@ -1,868 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import com.github.rnewson.couchdb.lucene.couchdb.*; -import com.github.rnewson.couchdb.lucene.util.*; -import org.apache.commons.configuration.HierarchicalINIConfiguration; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.HttpClient; -import org.apache.http.client.HttpResponseException; -import org.apache.http.client.ResponseHandler; -import org.apache.http.client.methods.HttpUriRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.document.Document; -import org.apache.lucene.index.*; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.queryparser.classic.QueryParser; -import org.apache.lucene.queryparser.classic.QueryParser.Operator; -import org.apache.lucene.search.*; -import org.apache.lucene.search.vectorhighlight.FastVectorHighlighter; -import org.apache.lucene.store.Directory; -import org.apache.lucene.store.FSDirectory; -import org.apache.lucene.store.SingleInstanceLockFactory; -import org.apache.lucene.util.BytesRef; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.mozilla.javascript.ClassShutter; -import org.mozilla.javascript.Context; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.*; -import java.net.SocketException; -import java.util.*; -import java.util.Map.Entry; -import java.util.concurrent.CountDownLatch; - -import static java.lang.Math.max; -import static java.util.concurrent.TimeUnit.SECONDS; - -public final class DatabaseIndexer implements Runnable, ResponseHandler { - - private class IndexState { - - private final DocumentConverter converter; - private boolean readerDirty; - private String etag; - - private final Analyzer analyzer; - private UpdateSequence pending_seq; - private DirectoryReader reader; - private final IndexWriter writer; - private final Database database; - private final View view; - - public IndexState(final DocumentConverter converter, - final IndexWriter writer, final Analyzer analyzer, - final Database database, final View view) { - this.converter = converter; - this.writer = writer; - this.analyzer = analyzer; - this.database = database; - this.view = view; - } - - public synchronized DirectoryReader borrowReader(final boolean staleOk) - throws IOException, JSONException { - blockForLatest(staleOk); - if (reader == null) { - etag = newEtag(); - } - - if (reader != null) { - reader.decRef(); - } - reader = DirectoryReader.open(writer, !staleOk, false); - if (readerDirty) { - etag = newEtag(); - readerDirty = false; - } - - reader.incRef(); - return reader; - } - - public IndexSearcher borrowSearcher(final boolean staleOk) - throws IOException, JSONException { - return new IndexSearcher(borrowReader(staleOk)); - } - - public void returnReader(final IndexReader reader) throws IOException { - reader.decRef(); - } - - public void returnSearcher(final IndexSearcher searcher) - throws IOException { - returnReader(searcher.getIndexReader()); - } - - public Query parse(final String query, final Operator operator, final Analyzer analyzer) throws ParseException, JSONException { - final QueryParser parser = new CustomQueryParser(Constants.DEFAULT_FIELD, analyzer); - parser.setDefaultOperator(operator); - parser.setAllowLeadingWildcard(ini.getBoolean("lucene.allowLeadingWildcard", false)); - parser.setLowercaseExpandedTerms(ini.getBoolean("lucene.lowercaseExpandedTerms", true)); - return parser.parse(query); - } - - public Analyzer analyzer(final String spec) throws JSONException { - return spec == null ? this.analyzer : Analyzers.fromSpec(spec); - } - - private synchronized void close() throws IOException { - if (reader != null) - reader.close(); - if (writer != null) - writer.rollback(); - } - - private synchronized String getEtag() { - return etag; - } - - public UUID getUuid() throws JSONException, IOException { - return database.getUuid(); - } - - public String getDigest() { - return view.getDigest(); - } - - private String newEtag() { - return Long.toHexString(now()); - } - - private synchronized boolean notModified(final HttpServletRequest req) { - return etag != null && etag.equals(req.getHeader("If-None-Match")); - } - - private void blockForLatest(final boolean staleOk) throws IOException, JSONException { - if (staleOk) { - return; - } - final UpdateSequence latest = database.getLastSequence(); - synchronized (this) { - long timeout = getSearchTimeout(); - while (pending_seq.isEarlierThan(latest)) { - try { - final long start = System.currentTimeMillis(); - wait(timeout); - timeout -= (System.currentTimeMillis() - start); - if (timeout <= 0) { - throw new IOException("Search timed out."); - } - } catch (final InterruptedException e) { - throw new IOException("Search timed out."); - } - } - } - } - - private synchronized void setPendingSequence(final UpdateSequence seq) { - pending_seq = seq; - notifyAll(); - } - - @Override - public String toString() { - return writer.getDirectory().toString(); - } - } - - private final class RestrictiveClassShutter implements ClassShutter { - - public boolean visibleToScripts(final String fullClassName) { - return false; - } - } - - public static File uuidDir(final File root, final UUID uuid) { - return new File(root, uuid.toString()); - } - - public static File viewDir(final File root, final UUID uuid, - final String digest, final boolean mkdirs) throws IOException { - final File uuidDir = uuidDir(root, uuid); - final File viewDir = new File(uuidDir, digest); - if (mkdirs) { - viewDir.mkdirs(); - } - return viewDir; - } - - private static long now() { - return System.nanoTime(); - } - - private final HttpClient client; - - private boolean closed; - - private Context context; - - private final Database database; - - private UpdateSequence ddoc_seq; - - private long lastCommit; - - private final CountDownLatch latch = new CountDownLatch(1); - - private final Logger logger; - - private final Map paths = new HashMap<>(); - - private HttpUriRequest req; - - private final File root; - - private UpdateSequence since; - - private final Map states = Collections - .synchronizedMap(new HashMap()); - - private UUID uuid; - - private final HierarchicalINIConfiguration ini; - - public DatabaseIndexer(final HttpClient client, final File root, - final Database database, final HierarchicalINIConfiguration ini) - throws IOException, JSONException { - this.client = client; - this.root = root; - this.database = database; - this.ini = ini; - this.logger = LoggerFactory.getLogger(DatabaseIndexer.class.getName() + "." - + database.getInfo().getName()); - } - - public void admin(final HttpServletRequest req, - final HttpServletResponse resp) throws IOException, JSONException { - final IndexState state = getState(req, resp); - if (state == null) - return; - final String command = new PathParts(req).getCommand(); - - if ("_expunge".equals(command)) { - logger.info("Expunging deletes from " + state); - state.writer.forceMergeDeletes(false); - resp.setStatus(202); - ServletUtils.sendJsonSuccess(req, resp); - return; - } - - if ("_optimize".equals(command)) { - logger.info("Optimizing " + state); - state.writer.forceMerge(1, false); - resp.setStatus(202); - ServletUtils.sendJsonSuccess(req, resp); - return; - } - - ServletUtils.sendJsonError(req, resp, 400, "bad_request"); - } - - public void awaitInitialization() { - try { - latch.await(); - } catch (final InterruptedException e) { - // Ignore. - } - } - - public Void handleResponse(final HttpResponse response) - throws ClientProtocolException, IOException { - final HttpEntity entity = response.getEntity(); - final BufferedReader reader = new BufferedReader(new InputStreamReader( - entity.getContent(), "UTF-8")); - String line; - loop: - while ((line = reader.readLine()) != null) { - maybeCommit(); - - // Heartbeat. - if (line.length() == 0) { - logger.trace("heartbeat"); - continue loop; - } - - try { - final JSONObject json = new JSONObject(line); - - if (json.has("error")) { - logger.warn("Indexing stopping due to error: " + json); - break loop; - } - - if (json.has("last_seq")) { - logger.info("End of changes detected."); - break loop; - } - - final UpdateSequence seq = UpdateSequence.parseUpdateSequence(json.getString("seq")); - final String id = json.getString("id"); - CouchDocument doc; - if (!json.isNull("doc")) { - doc = new CouchDocument(json.getJSONObject("doc")); - } else { - // include_docs=true doesn't work prior to 0.11. - try { - doc = database.getDocument(id); - } catch (final HttpResponseException e) { - switch (e.getStatusCode()) { - case HttpStatus.SC_NOT_FOUND: - doc = CouchDocument.deletedDocument(id); - break; - default: - logger.warn("Failed to fetch " + id); - break loop; - } - } - } - - if (id.startsWith("_design")) { - if (seq.isLaterThan(ddoc_seq)) { - logger.info("Exiting due to design document change."); - break loop; - } - } - - if (doc.isDeleted()) { - for (final IndexState state : states.values()) { - state.writer.deleteDocuments(new Term("_id", id)); - state.setPendingSequence(seq); - state.readerDirty = true; - } - } else { - for (final Entry entry : states.entrySet()) { - final View view = entry.getKey(); - final IndexState state = entry.getValue(); - - if (seq.isLaterThan(state.pending_seq)) { - final Collection docs; - try { - docs = state.converter.convert(doc, view - .getDefaultSettings(), database); - } catch (final Exception e) { - logger.warn(id + " caused " + e.getMessage()); - continue loop; - } - - state.writer.updateDocuments(new Term("_id", id), docs); - state.setPendingSequence(seq); - state.readerDirty = true; - } - } - } - } catch (final JSONException e) { - logger.error("JSON exception in changes loop", e); - break loop; - } - } - req.abort(); - return null; - } - - public void info(final HttpServletRequest req, - final HttpServletResponse resp) throws IOException, JSONException { - final IndexState state = getState(req, resp); - if (state == null) - return; - final DirectoryReader reader = state.borrowReader(true); - try { - final JSONObject result = new JSONObject(); - result.put("current", reader.isCurrent()); - result.put("disk_size", Utils.directorySize(reader.directory())); - result.put("doc_count", reader.numDocs()); - result.put("doc_del_count", reader.numDeletedDocs()); - result.put("uuid", state.getUuid()); - result.put("digest", state.getDigest()); - result.put("update_seq", getUpdateSequence(reader.getIndexCommit().getUserData())); - final JSONArray fields = new JSONArray(); - for (LeafReaderContext leaf : reader.leaves()) { - for (FieldInfo info : leaf.reader().getFieldInfos()) { - if (info.name.startsWith("_")) { - continue; - } - if (info.getIndexOptions() != IndexOptions.NONE) { - fields.put(info.name); - } - } - } - result.put("fields", fields); - result.put("version", reader.getVersion()); - result.put("ref_count", reader.getRefCount()); - - final JSONObject info = new JSONObject(); - info.put("code", 200); - info.put("json", result); - - ServletUtils.setResponseContentTypeAndEncoding(req, resp); - final Writer writer = resp.getWriter(); - try { - writer.write(result.toString()); - } finally { - writer.close(); - } - } finally { - state.returnReader(reader); - } - } - - public void run() { - if (closed) { - throw new IllegalStateException("closed!"); - } - - try { - init(); - } catch (final Exception e) { - logger.warn("Exiting after init() raised exception.", e); - close(); - return; - } - - try { - try { - final long changes_timeout = ini.getLong("lucene.changes_timeout", -1); - req = database.getChangesRequest(since, changes_timeout); - logger.info("Indexing from update_seq " + since); - client.execute(req, this); - } finally { - close(); - } - } catch (final SocketException e) { - // Ignored because req.abort() does this. - } catch (final Exception e) { - logger.warn("Exiting due to exception.", e); - close(); - } - } - - public void search(final HttpServletRequest req, - final HttpServletResponse resp) throws IOException, JSONException { - final IndexState state = getState(req, resp); - if (state == null) - return; - final IndexSearcher searcher = state.borrowSearcher(isStaleOk(req)); - final String etag = state.getEtag(); - final FastVectorHighlighter fvh = new FastVectorHighlighter(true, true); - final JSONArray result = new JSONArray(); - try { - if (state.notModified(req)) { - resp.setStatus(304); - return; - } - for (final String queryString : getQueryStrings(req)) { - final Analyzer analyzer = state.analyzer(req.getParameter("analyzer")); - final Operator operator = "and".equalsIgnoreCase(req.getParameter("default_operator")) - ? Operator.AND : Operator.OR; - final Query q = state.parse(queryString, operator, analyzer); - - final JSONObject queryRow = new JSONObject(); - queryRow.put("q", q.toString()); - if (getBooleanParameter(req, "debug")) { - queryRow.put("plan", QueryPlan.toPlan(q)); - queryRow.put("analyzer", analyzer.getClass()); - } - queryRow.put("etag", etag); - if (getBooleanParameter(req, "rewrite")) { - final Query rewritten_q = q.rewrite(searcher - .getIndexReader()); - queryRow.put("rewritten_q", rewritten_q.toString()); - - final JSONObject freqs = new JSONObject(); - - final Set terms = new HashSet<>(); - final Weight weight = rewritten_q.createWeight(searcher, false); - - weight.extractTerms(terms); - for (final Object term : terms) { - final int freq = searcher.getIndexReader().docFreq((Term) term); - freqs.put(term.toString(), freq); - } - queryRow.put("freqs", freqs); - } else { - // Perform the search. - final TopDocs td; - final StopWatch stopWatch = new StopWatch(); - - final boolean include_docs = getBooleanParameter(req, - "include_docs"); - final int highlights = getIntParameter(req, "highlights", 0); - final int highlight_length = max(getIntParameter(req, "highlight_length", 18), 18); // min for fast term vector highlighter is 18 - final boolean include_termvectors = getBooleanParameter(req, "include_termvectors"); - final int limit = getIntParameter(req, "limit", - ini.getInt("lucene.limit", 25)); - final Sort sort = CustomQueryParser.toSort(req - .getParameter("sort")); - final int skip = getIntParameter(req, "skip", 0); - - final Set fieldsToLoad; - if (req.getParameter("include_fields") == null) { - fieldsToLoad = null; - } else { - final String[] fields = Utils.splitOnCommas( - req.getParameter("include_fields")); - final List list = Arrays.asList(fields); - fieldsToLoad = new HashSet<>(list); - } - - if (sort == null) { - td = searcher.search(q, skip + limit); - } else { - td = searcher.search(q, skip + limit, sort); - } - stopWatch.lap("search"); - - // Fetch matches (if any). - final int max = Math.max(0, Math.min(td.totalHits - skip, - limit)); - final JSONArray rows = new JSONArray(); - final String[] fetch_ids = new String[max]; - for (int i = skip; i < skip + max; i++) { - final Document doc; - if (fieldsToLoad == null) { - doc = searcher.doc(td.scoreDocs[i].doc); - } else { - doc = searcher.doc(td.scoreDocs[i].doc, fieldsToLoad); - } - - final JSONObject row = new JSONObject(); - final JSONObject fields = new JSONObject(); - final JSONObject highlight_rows = new JSONObject(); - - // Include stored fields. - for (final IndexableField f : doc.getFields()) { - if (!f.fieldType().stored()) { - continue; - } - final String name = f.name(); - final Object value; - if (f.numericValue() != null) { - value = f.numericValue(); - } else { - value = f.stringValue(); - } - if (value != null) { - if ("_id".equals(name)) { - row.put("id", value); - } else { - if (!fields.has(name)) { - fields.put(name, value); - } else { - final Object obj = fields.get(name); - if (obj instanceof String || obj instanceof Number) { - final JSONArray arr = new JSONArray(); - arr.put(obj); - arr.put(value); - fields.put(name, arr); - } else { - assert obj instanceof JSONArray; - ((JSONArray) obj).put(value); - } - } - - if (highlights > 0) { - String[] frags = fvh.getBestFragments(fvh.getFieldQuery(q), searcher.getIndexReader(), td.scoreDocs[i].doc, name, highlight_length, highlights); - highlight_rows.put(name, frags); - } - } - } - } - - if (!Float.isNaN(td.scoreDocs[i].score)) { - row.put("score", td.scoreDocs[i].score); - }// Include sort order (if any). - if (td instanceof TopFieldDocs) { - final FieldDoc fd = (FieldDoc) ((TopFieldDocs) td).scoreDocs[i]; - final JSONArray arr = new JSONArray(); - for (final Object o : fd.fields) { - if (o instanceof BytesRef) { - arr.put(((BytesRef)o).utf8ToString()); - } else { - arr.put(o); - } - } - row.put("sort_order", arr); - } - // Fetch document (if requested). - if (include_docs) { - fetch_ids[i - skip] = doc.get("_id"); - } - if (fields.length() > 0) { - row.put("fields", fields); - } - if (highlight_rows.length() > 0) { - row.put("highlights", highlight_rows); - } - - rows.put(row); - } - // Fetch documents (if requested). - if (include_docs && fetch_ids.length > 0) { - final List fetched_docs = database - .getDocuments(fetch_ids); - for (int j = 0; j < max; j++) { - final CouchDocument doc = fetched_docs.get(j); - final JSONObject row = doc == null ? - new JSONObject("{\"error\":\"not_found\"}") : - doc.asJson(); - rows.getJSONObject(j).put("doc", row); - } - } - stopWatch.lap("fetch"); - - queryRow.put("skip", skip); - queryRow.put("limit", limit); - queryRow.put("total_rows", td.totalHits); - queryRow.put("search_duration", stopWatch - .getElapsed("search")); - queryRow.put("fetch_duration", stopWatch - .getElapsed("fetch")); - // Include sort info (if requested). - if (td instanceof TopFieldDocs) { - queryRow.put("sort_order", CustomQueryParser - .toJSON(((TopFieldDocs) td).fields)); - } - queryRow.put("rows", rows); - } - result.put(queryRow); - } - } catch (final ParseException e) { - ServletUtils.sendJsonError(req, resp, 400, "Bad query syntax: " - + e.getMessage()); - return; - } finally { - state.returnSearcher(searcher); - } - - resp.setHeader("ETag", etag); - resp.setHeader("Cache-Control", "must-revalidate"); - ServletUtils.setResponseContentTypeAndEncoding(req, resp); - - final Object json = result.length() > 1 ? result : result.getJSONObject(0); - final String callback = req.getParameter("callback"); - final String body; - if (callback != null) { - body = String.format("%s(%s)", callback, json); - } else { - if (json instanceof JSONObject) { - final JSONObject obj = (JSONObject) json; - body = getBooleanParameter(req, "debug") ? - obj.toString(2) : obj.toString(); - } else { - final JSONArray arr = (JSONArray) json; - body = getBooleanParameter(req, "debug") ? - arr.toString(2) : arr.toString(); - } - } - - final Writer writer = resp.getWriter(); - try { - writer.write(body); - } finally { - writer.close(); - } - } - - private String[] getQueryStrings(final HttpServletRequest req) { - return Utils.splitOnCommas(req.getParameter("q")); - } - - private void close() { - this.closed = true; - - for (final IndexState state : states.values()) { - try { - state.close(); - } catch (final IOException e) { - logger.warn("Error while closing.", e); - } - } - states.clear(); - if (context != null) { - Context.exit(); - context = null; - } - latch.countDown(); - } - - public boolean isClosed() { - return closed; - } - - private void commitAll() throws IOException { - for (final Entry entry : states.entrySet()) { - final View view = entry.getKey(); - final IndexState state = entry.getValue(); - - if (state.pending_seq.isLaterThan(getUpdateSequence(state.writer))) { - final Map userData = new HashMap<>(); - userData.put("last_seq", state.pending_seq.toString()); - state.writer.setCommitData(userData); - state.writer.commit(); - logger.info(view + " now at update_seq " + state.pending_seq); - } - } - lastCommit = now(); - } - - private static boolean getBooleanParameter(final HttpServletRequest req, - final String parameterName) { - return Boolean.parseBoolean(req.getParameter(parameterName)); - } - - private static int getIntParameter(final HttpServletRequest req, - final String parameterName, final int defaultValue) { - final String result = req.getParameter(parameterName); - return result != null ? Integer.parseInt(result) : defaultValue; - } - - private IndexState getState(final HttpServletRequest req, - final HttpServletResponse resp) throws IOException, JSONException { - final View view = paths.get(toPath(req)); - if (view == null) { - ServletUtils.sendJsonError(req, resp, 400, "no_such_view"); - return null; - } - - final IndexState result = states.get(view); - if (result == null) { - ServletUtils.sendJsonError(req, resp, 400, "no_such_state"); - } - return result; - } - - private UpdateSequence getUpdateSequence(final Directory dir) throws IOException { - if (!DirectoryReader.indexExists(dir)) { - return UpdateSequence.START; - } - final List commits = DirectoryReader.listCommits(dir); - final IndexCommit latest = commits.get(commits.size() - 1); - return getUpdateSequence(latest.getUserData()); - } - - private UpdateSequence getUpdateSequence(final IndexWriter writer) throws IOException { - return getUpdateSequence(writer.getDirectory()); - } - - private UpdateSequence getUpdateSequence(final Map userData) { - if (userData != null && userData.containsKey("last_seq")) { - return UpdateSequence.parseUpdateSequence(userData.get("last_seq")); - } - return UpdateSequence.START; - } - - private void init() throws IOException, JSONException { - this.uuid = database.getOrCreateUuid(); - - this.context = Context.enter(); - context.setClassShutter(new RestrictiveClassShutter()); - context.setOptimizationLevel(9); - - this.ddoc_seq = database.getInfo().getUpdateSequence(); - this.since = null; - - for (final DesignDocument ddoc : database.getAllDesignDocuments()) { - for (final Entry entry : ddoc.getAllViews() - .entrySet()) { - final String name = entry.getKey(); - final View view = entry.getValue(); - paths.put(toPath(ddoc.getId(), name), view); - - if (!states.containsKey(view)) { - final Directory dir = FSDirectory.open(viewDir(view, true).toPath(), - new SingleInstanceLockFactory()); - final UpdateSequence seq = getUpdateSequence(dir); - if (since == null) { - since = seq; - } - since = seq.isEarlierThan(since) ? seq : since; - logger.debug(dir + " bumped since to " + since); - - final DocumentConverter converter = new DocumentConverter( - context, view); - final IndexWriter writer = newWriter(dir, view.getAnalyzer()); - - final IndexState state = new IndexState(converter, writer, - view.getAnalyzer(), database, view); - state.setPendingSequence(seq); - states.put(view, state); - } - } - } - if (since == null) { - since = UpdateSequence.START; - } - logger.debug("paths: " + paths); - - this.lastCommit = now(); - latch.countDown(); - } - - private static boolean isStaleOk(final HttpServletRequest req) { - return "ok".equals(req.getParameter("stale")); - } - - private void maybeCommit() throws IOException { - if (now() - lastCommit >= getCommitInterval()) { - commitAll(); - } - } - - private IndexWriter newWriter(final Directory dir, final Analyzer analyzer) throws IOException { - final IndexWriterConfig config = new IndexWriterConfig(analyzer); - config.setUseCompoundFile(ini.getBoolean("lucene.useCompoundFile", - false)); - config.setRAMBufferSizeMB(ini.getDouble("lucene.ramBufferSizeMB", - IndexWriterConfig.DEFAULT_RAM_BUFFER_SIZE_MB)); - - return new IndexWriter(dir, config); - } - - private File viewDir(final View view, final boolean mkdirs) - throws IOException { - return viewDir(root, uuid, view.getDigest(), mkdirs); - } - - private long getSearchTimeout() { - return ini.getLong("lucene.timeout", 5000); - } - - private long getCommitInterval() { - final long commitSeconds = max(1L, ini - .getLong("lucene.commitEvery", 15)); - return SECONDS.toNanos(commitSeconds); - } - - private String toPath(final HttpServletRequest req) { - final PathParts parts = new PathParts(req); - return toPath(parts.getDesignDocumentName(), parts.getViewName()); - } - - private static String toPath(final String ddoc, final String view) { - return ddoc + "/" + view; - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/DocumentConverter.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/DocumentConverter.java deleted file mode 100644 index 6d16029518..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/DocumentConverter.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import com.github.rnewson.couchdb.lucene.couchdb.CouchDocument; -import com.github.rnewson.couchdb.lucene.couchdb.Database; -import com.github.rnewson.couchdb.lucene.couchdb.View; -import com.github.rnewson.couchdb.lucene.couchdb.ViewSettings; -import com.github.rnewson.couchdb.lucene.rhino.JSLog; -import com.github.rnewson.couchdb.lucene.rhino.RhinoDocument; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.apache.lucene.document.Document; -import org.apache.lucene.queryparser.classic.ParseException; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.mozilla.javascript.*; - -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; - -public final class DocumentConverter { - - private static final Collection NO_DOCUMENTS = Collections.emptyList(); - private static final Logger LOG = LoggerFactory.getLogger(DocumentConverter.class); - - private final Context context; - private final Function viewFun; - private final ScriptableObject scope; - - public DocumentConverter(final Context context, final View view) throws IOException, JSONException { - this.context = context; - scope = context.initStandardObjects(); - context.setLanguageVersion(Context.VERSION_1_8); - - // Allow custom document helper class. - try { - ScriptableObject.defineClass(scope, RhinoDocument.class); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InstantiationException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - - // Add a log object - ScriptableObject.putProperty(scope, "log", new JSLog()); - - // Compile user-specified function - try { - viewFun = view.compileFunction(context, scope); - } catch (final RhinoException e) { - LOG.error("View code for " + view + " does not compile."); - throw e; - } - } - - public Collection convert( - final CouchDocument doc, - final ViewSettings defaults, - final Database database) throws IOException, ParseException, JSONException { - final Object result; - final Scriptable scriptable = convertObject(doc.asJson()); - - try { - result = viewFun.call(context, scope, null, new Object[]{scriptable}); - } catch (final JavaScriptException e) { - LOG.warn(doc + " caused exception during conversion.", e); - return NO_DOCUMENTS; - } - - if (result == null || result instanceof Undefined) { - return NO_DOCUMENTS; - } - - if (result instanceof RhinoDocument) { - final RhinoDocument rhinoDocument = (RhinoDocument) result; - final Document document = rhinoDocument.toDocument(doc.getId(), defaults, database); - return Collections.singleton(document); - } - - if (result instanceof NativeArray) { - final NativeArray nativeArray = (NativeArray) result; - final Collection arrayResult = new ArrayList<>((int) nativeArray.getLength()); - for (int i = 0; i < (int) nativeArray.getLength(); i++) { - if (nativeArray.get(i, null) instanceof RhinoDocument) { - final RhinoDocument rhinoDocument = (RhinoDocument) nativeArray.get(i, null); - final Document document = rhinoDocument.toDocument( - doc.getId(), - defaults, - database); - arrayResult.add(document); - } - } - return arrayResult; - } - - return null; - } - - private Object convert(final Object obj) throws JSONException { - if (obj instanceof JSONArray) { - return convertArray((JSONArray) obj); - } else if (obj == JSONObject.NULL) { - return null; - } else if (obj instanceof JSONObject) { - return convertObject((JSONObject) obj); - } else { - return obj; - } - } - - private Scriptable convertArray(final JSONArray array) throws JSONException { - final Scriptable result = context.newArray(scope, array.length()); - for (int i = 0, max = array.length(); i < max; i++) { - ScriptableObject.putProperty(result, i, convert(array.get(i))); - } - return result; - } - - private Scriptable convertObject(final JSONObject obj) throws JSONException { - if (obj == JSONObject.NULL) { - return null; - } - final Scriptable result = context.newObject(scope); - final Iterator it = obj.keys(); - while (it.hasNext()) { - final String key = (String) it.next(); - final Object value = obj.get(key); - ScriptableObject.putProperty(result, key, convert(value)); - } - return result; - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/HttpClientFactory.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/HttpClientFactory.java deleted file mode 100644 index 386fae1b18..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/HttpClientFactory.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import org.apache.commons.configuration.HierarchicalINIConfiguration; -import org.apache.http.*; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.AuthState; -import org.apache.http.auth.Credentials; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.client.HttpClient; -import org.apache.http.client.protocol.ClientContext; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.ClientConnectionRequest; -import org.apache.http.conn.ManagedClientConnection; -import org.apache.http.conn.params.ConnManagerParams; -import org.apache.http.conn.params.ConnPerRouteBean; -import org.apache.http.conn.routing.HttpRoute; -import org.apache.http.conn.scheme.PlainSocketFactory; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.ssl.SSLSocketFactory; -import org.apache.http.impl.auth.BasicScheme; -import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpProtocolParams; -import org.apache.http.protocol.ExecutionContext; -import org.apache.http.protocol.HttpContext; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Iterator; -import java.util.concurrent.TimeUnit; - -/** - * HttpClient instances just the way we like them. - * - * @author rnewson - */ -public final class HttpClientFactory { - - private HttpClientFactory () { - throw new InstantiationError("This class is not supposed to be instantiated."); - } - - private static final class PreemptiveAuthenticationRequestInterceptor - implements HttpRequestInterceptor { - - public void process(final HttpRequest request, final HttpContext context) - throws HttpException, IOException { - - final AuthState authState = (AuthState) context - .getAttribute(ClientContext.TARGET_AUTH_STATE); - final CredentialsProvider credsProvider = (CredentialsProvider) context - .getAttribute(ClientContext.CREDS_PROVIDER); - final HttpHost targetHost = (HttpHost) context - .getAttribute(ExecutionContext.HTTP_TARGET_HOST); - - // If not auth scheme has been initialized yet - if (authState.getAuthScheme() == null) { - AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort()); - // Obtain credentials matching the target host - Credentials creds = credsProvider.getCredentials(authScope); - // If found, generate BasicScheme preemptively - if (creds != null) { - authState.setAuthScheme(new BasicScheme()); - authState.setCredentials(creds); - } - } - } - } - - private static class ShieldedClientConnManager implements ClientConnectionManager { - - private final ClientConnectionManager delegate; - - public ShieldedClientConnManager(final ClientConnectionManager delegate) { - this.delegate = delegate; - } - - public void closeExpiredConnections() { - delegate.closeExpiredConnections(); - } - - public void closeIdleConnections(final long idletime, final TimeUnit tunit) { - delegate.closeIdleConnections(idletime, tunit); - } - - public SchemeRegistry getSchemeRegistry() { - return delegate.getSchemeRegistry(); - } - - public void releaseConnection( - final ManagedClientConnection conn, - final long validDuration, - final TimeUnit timeUnit) { - delegate.releaseConnection(conn, validDuration, timeUnit); - } - - public ClientConnectionRequest requestConnection(final HttpRoute route, final Object state) { - return delegate.requestConnection(route, state); - } - - public void shutdown() { - // SHIELDED. - // delegate.shutdown(); - } - - } - - private static DefaultHttpClient instance; - - private static HierarchicalINIConfiguration INI; - - public static synchronized HttpClient getInstance() throws MalformedURLException { - if (instance == null) { - final HttpParams params = new BasicHttpParams(); - // protocol params. - HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); - HttpProtocolParams.setUseExpectContinue(params, false); - // connection params. - HttpConnectionParams.setTcpNoDelay(params, true); - HttpConnectionParams.setStaleCheckingEnabled(params, false); - ConnManagerParams.setMaxTotalConnections(params, 1000); - ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(1000)); - - final SchemeRegistry schemeRegistry = new SchemeRegistry(); - schemeRegistry - .register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 5984)); - schemeRegistry - .register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443)); - final ClientConnectionManager cm = new ShieldedClientConnManager( - new ThreadSafeClientConnManager(params, schemeRegistry)); - - instance = new DefaultHttpClient(cm, params); - - if (INI != null) { - final CredentialsProvider credsProvider = new BasicCredentialsProvider(); - final Iterator it = INI.getKeys(); - while (it.hasNext()) { - final String key = (String) it.next(); - if (!key.startsWith("lucene.") && key.endsWith(".url")) { - final URL url = new URL(INI.getString(key)); - if (url.getUserInfo() != null) { - credsProvider.setCredentials( - new AuthScope(url.getHost(), url.getPort()), - new UsernamePasswordCredentials(url.getUserInfo())); - } - } - } - instance.setCredentialsProvider(credsProvider); - instance.addRequestInterceptor(new PreemptiveAuthenticationRequestInterceptor(), 0); - } - } - return instance; - } - - public static void setIni(final HierarchicalINIConfiguration ini) { - INI = ini; - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/IndexKey.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/IndexKey.java deleted file mode 100644 index 1532330103..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/IndexKey.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import javax.servlet.http.HttpServletRequest; - -@Deprecated -public class IndexKey { - - private final String key; - - private final String database; - - private final String ddoc; - - private final String view; - - public static IndexKey parse(final HttpServletRequest req) { - final String[] parts = req.getRequestURI().replaceFirst("/", "").split( - "/"); - if (parts.length < 4) { - return null; - } - return new IndexKey(parts[0], parts[1], parts[2], parts[3]); - } - - public IndexKey(String key, String database, String ddoc, String view) { - this.key = key; - this.database = database; - this.ddoc = ddoc; - this.view = view; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((database == null) ? 0 : database.hashCode()); - result = prime * result + ((ddoc == null) ? 0 : ddoc.hashCode()); - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((view == null) ? 0 : view.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - IndexKey other = (IndexKey) obj; - if (database == null) { - if (other.database != null) - return false; - } else if (!database.equals(other.database)) - return false; - if (ddoc == null) { - if (other.ddoc != null) - return false; - } else if (!ddoc.equals(other.ddoc)) - return false; - if (key == null) { - if (other.key != null) - return false; - } else if (!key.equals(other.key)) - return false; - if (view == null) { - if (other.view != null) - return false; - } else if (!view.equals(other.view)) - return false; - return true; - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/JSONErrorHandler.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/JSONErrorHandler.java deleted file mode 100644 index a2da36a6af..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/JSONErrorHandler.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import com.github.rnewson.couchdb.lucene.util.ServletUtils; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.ErrorHandler; -import org.json.JSONException; -import org.json.JSONObject; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * Convert errors to CouchDB-style JSON objects. - * - * @author rnewson - */ -public final class JSONErrorHandler extends ErrorHandler { - - public void handle(String target, - Request baseRequest, - HttpServletRequest request, - HttpServletResponse response) throws IOException { - final String reason = baseRequest.getResponse().getReason(); - try { - if (reason != null && reason.startsWith("{")) { - ServletUtils.sendJsonError(request, response, baseRequest.getResponse().getStatus(), - new JSONObject(reason)); - } else { - ServletUtils.sendJsonError(request, response, baseRequest.getResponse().getStatus(), - reason); - } - } catch (final JSONException e) { - response.sendError(500); - } - - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/LuceneServlet.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/LuceneServlet.java deleted file mode 100644 index 641b8c2210..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/LuceneServlet.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import com.github.rnewson.couchdb.lucene.couchdb.Couch; -import com.github.rnewson.couchdb.lucene.couchdb.Database; -import com.github.rnewson.couchdb.lucene.couchdb.DesignDocument; -import com.github.rnewson.couchdb.lucene.couchdb.View; -import com.github.rnewson.couchdb.lucene.util.ServletUtils; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.commons.configuration.HierarchicalINIConfiguration; -import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.StringUtils; -import org.apache.http.client.HttpClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.*; - -import static com.github.rnewson.couchdb.lucene.util.ServletUtils.getUri; - -public final class LuceneServlet extends HttpServlet { - - private static final Logger LOG = LoggerFactory.getLogger(LuceneServlet.class); - - private static final long serialVersionUID = 1L; - - private final HttpClient client; - - private final Map indexers = new HashMap<>(); - - private final HierarchicalINIConfiguration ini; - - private final File root; - - private final Map threads = new HashMap<>(); - - public LuceneServlet() throws ConfigurationException, IOException { - final Config config = new Config(); - this.client = config.getClient(); - this.root = config.getDir(); - this.ini = config.getConfiguration(); - } - - public LuceneServlet(final HttpClient client, final File root, - final HierarchicalINIConfiguration ini) { - this.client = client; - this.root = root; - this.ini = ini; - } - - private void cleanup(final HttpServletRequest req, - final HttpServletResponse resp) throws IOException, JSONException { - final Couch couch = getCouch(req); - final Set dbKeep = new HashSet<>(); - final JSONArray databases = couch.getAllDatabases(); - for (int i = 0; i < databases.length(); i++) { - final Database db = couch.getDatabase(databases.getString(i)); - final UUID uuid = db.getUuid(); - if (uuid == null) { - continue; - } - dbKeep.add(uuid.toString()); - - final Set viewKeep = new HashSet<>(); - - for (final DesignDocument ddoc : db.getAllDesignDocuments()) { - for (final View view : ddoc.getAllViews().values()) { - viewKeep.add(view.getDigest()); - } - } - - // Delete all indexes except the keepers. - final File[] dirs = DatabaseIndexer.uuidDir(root, db.getUuid()).listFiles(); - if (dirs != null) { - for (final File dir : dirs) { - if (!viewKeep.contains(dir.getName())) { - LOG.info("Cleaning old index at " + dir); - FileUtils.deleteDirectory(dir); - } - } - } - } - - // Delete all directories except the keepers. - for (final File dir : root.listFiles()) { - if (!dbKeep.contains(dir.getName())) { - LOG.info("Cleaning old index at " + dir); - FileUtils.deleteDirectory(dir); - } - } - - resp.setStatus(202); - ServletUtils.sendJsonSuccess(req, resp); - } - - private Couch getCouch(final HttpServletRequest req) throws IOException { - final String sectionName = new PathParts(req).getKey(); - final Configuration section = ini.getSection(sectionName); - if (!section.containsKey("url")) { - throw new FileNotFoundException(sectionName + " is missing or has no url parameter."); - } - return new Couch(client, section.getString("url")); - } - - private synchronized DatabaseIndexer getIndexer(final Database database) - throws IOException, JSONException { - DatabaseIndexer result = indexers.get(database); - Thread thread = threads.get(database); - if (result == null || thread == null || !thread.isAlive()) { - result = new DatabaseIndexer(client, root, database, ini); - thread = new Thread(result); - thread.start(); - result.awaitInitialization(); - if (result.isClosed()) { - return null; - } else { - indexers.put(database, result); - threads.put(database, thread); - } - } - - return result; - } - - private DatabaseIndexer getIndexer(final HttpServletRequest req) - throws IOException, JSONException { - final Couch couch = getCouch(req); - final Database database = couch.getDatabase(new PathParts(req) - .getDatabaseName()); - return getIndexer(database); - } - - private void handleWelcomeReq(final HttpServletRequest req, - final HttpServletResponse resp) throws ServletException, - IOException, JSONException { - final Package p = this.getClass().getPackage(); - final JSONObject welcome = new JSONObject(); - welcome.put("couchdb-lucene", "Welcome"); - welcome.put("version", p.getImplementationVersion()); - ServletUtils.sendJson(req, resp, welcome); - } - - @Override - protected void doGet(final HttpServletRequest req, - final HttpServletResponse resp) throws ServletException, - IOException { - try { - doGetInternal(req, resp); - } catch (final JSONException e) { - resp.sendError(500); - } - } - - private void doGetInternal(final HttpServletRequest req, final HttpServletResponse resp) - throws ServletException, IOException, JSONException { - switch (StringUtils.countMatches(getUri(req), "/")) { - case 1: - handleWelcomeReq(req, resp); - return; - case 5: - final DatabaseIndexer indexer = getIndexer(req); - if (indexer == null) { - ServletUtils.sendJsonError(req, resp, 500, "error_creating_index"); - return; - } - - if (req.getParameter("q") == null) { - indexer.info(req, resp); - } else { - indexer.search(req, resp); - } - return; - } - - ServletUtils.sendJsonError(req, resp, 400, "bad_request"); - } - - @Override - protected void doPost(final HttpServletRequest req, - final HttpServletResponse resp) throws ServletException, - IOException { - try { - doPostInternal(req, resp); - } catch (final JSONException e) { - resp.sendError(500); - } - } - - private void doPostInternal(final HttpServletRequest req, final HttpServletResponse resp) - throws IOException, JSONException { - switch (StringUtils.countMatches(getUri(req), "/")) { - case 3: - if (req.getRequestURI().endsWith("/_cleanup")) { - cleanup(req, resp); - return; - } - break; - case 5: { - final DatabaseIndexer indexer = getIndexer(req); - if (indexer == null) { - ServletUtils.sendJsonError(req, resp, 500, "error_creating_index"); - return; - } - indexer.search(req, resp); - break; - } - case 6: - final DatabaseIndexer indexer = getIndexer(req); - indexer.admin(req, resp); - return; - } - ServletUtils.sendJsonError(req, resp, 400, "bad_request"); - } - - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/Main.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/Main.java deleted file mode 100644 index cf681df62f..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/Main.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.eclipse.jetty.server.handler.gzip.GzipHandler; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.servlet.FilterHolder; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; - -import javax.servlet.DispatcherType; -import java.io.File; -import java.util.EnumSet; - -public final class Main { - - private static final Logger LOG = LoggerFactory.getLogger(Main.class); - - private Main() { - throw new InstantiationError("This class is not supposed to be instantiated."); - } - /** - * Run couchdb-lucene. - */ - public static void main(String[] args) throws Exception { - final Config config = new Config(); - final File dir = config.getDir(); - - final Server server = new Server(); - - final ServerConnector connector = new ServerConnector(server); - connector.setHost(config.getConfiguration().getString("lucene.host", "localhost")); - connector.setPort(config.getConfiguration().getInt("lucene.port", 5985)); - - LOG.info("Accepting connections with " + connector); - - server.addConnector(connector); - server.setStopAtShutdown(true); - - final LuceneServlet servlet = new LuceneServlet(config.getClient(), dir, config.getConfiguration()); - - final ServletContextHandler context = new ServletContextHandler(server, "/", - ServletContextHandler.NO_SESSIONS | ServletContextHandler.NO_SECURITY); - context.addServlet(new ServletHolder(servlet), "/*"); - context.setErrorHandler(new JSONErrorHandler()); - server.setHandler(context); - - server.start(); - server.join(); - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/PathParts.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/PathParts.java deleted file mode 100644 index b4bd898afe..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/PathParts.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import javax.servlet.http.HttpServletRequest; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static com.github.rnewson.couchdb.lucene.util.ServletUtils.getUri; - -public class PathParts { - - private static final Pattern QUERY_REGEX = Pattern - .compile("^/([^/]+)/([^/]+)/_design/([^/]+)/([^/]+)/?([^/]+)?"); - - private static final Pattern GLOBAL_REGEX = Pattern - .compile("^/([^/]+)/([^/]+)/((([^/]+)))"); - - private Matcher matcher; - - public PathParts(final HttpServletRequest req) { - this(getUri(req)); - } - - public PathParts(final String path) { - matcher = QUERY_REGEX.matcher(path); - if (!matcher.matches()) { - matcher = GLOBAL_REGEX.matcher(path); - } - if (!matcher.matches()) { - throw new IllegalArgumentException(path + " is not a valid path"); - } - } - - public String getKey() { - return matcher.group(1); - } - - public String getDesignDocumentName() { - return "_design/" + matcher.group(3); - } - - public String getDatabaseName() { - return matcher.group(2); - } - - public String getViewName() { - return matcher.group(4); - } - - public String getCommand() { - if (matcher.groupCount() != 5) { - return null; - } - return matcher.group(5); - } - - @Override - public String toString() { - return "PathParts [getCommand()=" + getCommand() - + ", getDatabaseName()=" + getDatabaseName() - + ", getDesignDocumentName()=" + getDesignDocumentName() - + ", getKey()=" + getKey() + ", getViewName()=" + getViewName() - + "]"; - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/QueryPlan.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/QueryPlan.java deleted file mode 100644 index 20ecad44d8..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/QueryPlan.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import org.apache.lucene.search.*; - -/** - * this class converts Query objects to a textual description of the classes - * used to execute it. - * - * @author rnewson - */ -public final class QueryPlan { - - private QueryPlan() { - - } - - /** - * Produces a string representation of the query classes used for a query. - * - * @param query - * @return - */ - public static String toPlan(final Query query) { - final StringBuilder builder = new StringBuilder(300); - toPlan(builder, query); - return builder.toString(); - } - - private static void planBooleanQuery(final StringBuilder builder, final BooleanQuery query) { - for (final BooleanClause clause : query.clauses()) { - builder.append(clause.getOccur()); - toPlan(builder, clause.getQuery()); - } - } - - private static void planFuzzyQuery(final StringBuilder builder, final FuzzyQuery query) { - builder.append(query.getTerm()); - builder.append(",prefixLength="); - builder.append(query.getPrefixLength()); - builder.append(",maxEdits="); - builder.append(query.getMaxEdits()); - } - - private static void planPrefixQuery(final StringBuilder builder, final PrefixQuery query) { - builder.append(query.getPrefix()); - } - - private static void planTermQuery(final StringBuilder builder, final TermQuery query) { - builder.append(query.getTerm()); - } - - private static void planTermRangeQuery(final StringBuilder builder, final TermRangeQuery query) { - builder.append(query.getLowerTerm().utf8ToString()); - builder.append(" TO "); - builder.append(query.getUpperTerm().utf8ToString()); - } - - private static void planWildcardQuery(final StringBuilder builder, final WildcardQuery query) { - builder.append(query.getTerm()); - } - - private static void planBoostQuery(final StringBuilder builder, final BoostQuery query) { - toPlan(builder, query.getQuery()); - builder.append(",boost=" + query.getBoost() + ")"); - } - - private static void toPlan(final StringBuilder builder, final Query query) { - builder.append(query.getClass().getSimpleName()); - builder.append("("); - if (query instanceof TermQuery) { - planTermQuery(builder, (TermQuery) query); - } else if (query instanceof BooleanQuery) { - planBooleanQuery(builder, (BooleanQuery) query); - } else if (query instanceof TermRangeQuery) { - planTermRangeQuery(builder, (TermRangeQuery) query); - } else if (query instanceof PrefixQuery) { - planPrefixQuery(builder, (PrefixQuery) query); - } else if (query instanceof WildcardQuery) { - planWildcardQuery(builder, (WildcardQuery) query); - } else if (query instanceof FuzzyQuery) { - planFuzzyQuery(builder, (FuzzyQuery) query); - } else if (query instanceof BoostQuery) { - planBoostQuery(builder, (BoostQuery) query); - } else { - builder.append(query.getClass()); - builder.append("@"); - builder.append(query); - } - builder.append(")"); - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/Tika.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/Tika.java deleted file mode 100644 index 6c1ba4d1a3..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/Tika.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.apache.lucene.document.Document; -import org.apache.tika.exception.TikaException; -import org.apache.tika.metadata.DublinCore; -import org.apache.tika.metadata.HttpHeaders; -import org.apache.tika.metadata.Metadata; -import org.apache.tika.metadata.Property; - -import java.io.IOException; -import java.io.InputStream; - -import static com.github.rnewson.couchdb.lucene.util.Utils.text; - -public final class Tika { - - public static final Tika INSTANCE = new Tika(); - - private static final String DC = "_dc."; - - private static final Logger log = LoggerFactory.getLogger(Tika.class); - - private final org.apache.tika.Tika tika = new org.apache.tika.Tika(); - - private Tika() { - tika.setMaxStringLength(-1); - } - - public void parse(final InputStream in, final String contentType, final String fieldName, final Document doc) - throws IOException { - final Metadata md = new Metadata(); - md.set(HttpHeaders.CONTENT_TYPE, contentType); - - try { - // Add body text. - doc.add(text(fieldName, tika.parseToString(in, md), false)); - } catch (final IOException e) { - log.warn("Failed to index an attachment.", e); - return; - } catch (final TikaException e) { - log.warn("Failed to parse an attachment.", e); - return; - } - - // Add DC attributes. - addDublinCoreAttributes(md, doc); - } - - private void addAttribute(final String namespace, final String attributeName, final Metadata md, final Document doc) { - if (md.get(attributeName) != null) { - doc.add(text(namespace + attributeName, md.get(attributeName), false)); - } - } - - private void addAttribute(final String namespace, final Property property, final Metadata md, final Document doc) { - if (md.get(property) != null) { - doc.add(text(namespace + property.getName(), md.get(property), false)); - } - } - - private void addDublinCoreAttributes(final Metadata md, final Document doc) { - addAttribute(DC, DublinCore.CONTRIBUTOR, md, doc); - addAttribute(DC, DublinCore.COVERAGE, md, doc); - addAttribute(DC, DublinCore.CREATOR, md, doc); - addAttribute(DC, DublinCore.DATE, md, doc); - addAttribute(DC, DublinCore.DESCRIPTION, md, doc); - addAttribute(DC, DublinCore.FORMAT, md, doc); - addAttribute(DC, DublinCore.IDENTIFIER, md, doc); - addAttribute(DC, DublinCore.LANGUAGE, md, doc); - addAttribute(DC, DublinCore.MODIFIED, md, doc); - addAttribute(DC, DublinCore.PUBLISHER, md, doc); - addAttribute(DC, DublinCore.RELATION, md, doc); - addAttribute(DC, DublinCore.RIGHTS, md, doc); - addAttribute(DC, DublinCore.SOURCE, md, doc); - addAttribute(DC, DublinCore.SUBJECT, md, doc); - addAttribute(DC, DublinCore.TITLE, md, doc); - addAttribute(DC, DublinCore.TYPE, md, doc); - } -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/TypedField.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/TypedField.java deleted file mode 100644 index 5e28a81170..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/TypedField.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import com.github.rnewson.couchdb.lucene.couchdb.FieldType; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.SortField; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public final class TypedField { - - private static Pattern PATTERN = Pattern.compile("^([^<]+)(<([^>]+)>)?$"); - - private final String name; - - private final FieldType type; - - public TypedField(final String string) throws ParseException { - final Matcher matcher = PATTERN.matcher(string); - - if (!matcher.matches()) { - throw new ParseException("Field '" + string + "' not recognized."); - } - - this.name = matcher.group(1); - try { - this.type = matcher.group(3) == null ? FieldType.TEXT : FieldType.valueOf(matcher.group(3).toUpperCase()); - } catch (final IllegalArgumentException e) { - throw new ParseException("Unrecognized type '" + matcher.group(3) + "'"); - } - } - - public String getName() { - return name; - } - - public FieldType getType() { - return type; - } - - public SortField.Type toSortField() { - return type.toType(); - } - - public Query toRangeQuery(final String lower, final String upper, - final boolean lowerInclusive, final boolean upperInclusive) - throws ParseException { - return type.toRangeQuery(name, lower, upper, lowerInclusive, upperInclusive); - } - - public Query toTermQuery(final String text) throws ParseException { - return type.toTermQuery(name, text); - } - - @Override - public String toString() { - return String.format("%s<%s>", name, type.toString().toLowerCase()); - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/Couch.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/Couch.java deleted file mode 100644 index 44e7e573c6..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/Couch.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.couchdb; - -import org.apache.http.client.HttpClient; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.IOException; - -/** - * Simple Java API access to CouchDB. - * - * @author rnewson - */ -public class Couch { - - private final HttpClient httpClient; - private final String url; - - public Couch(final HttpClient httpClient, final String url) - throws IOException { - this.httpClient = httpClient; - this.url = url.endsWith("/") ? url : url + "/"; - } - - public final JSONArray getAllDatabases() throws IOException, JSONException { - final String response = HttpUtils.get(httpClient, url + "_all_dbs"); - return new JSONArray(response); - } - - public final JSONObject getInfo() throws IOException, JSONException { - return new JSONObject(HttpUtils.get(httpClient, url)); - } - - public Database getDatabase(final String dbname) throws IOException { - return new Database(httpClient, url + dbname); - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/CouchDocument.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/CouchDocument.java deleted file mode 100644 index abbd041757..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/CouchDocument.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.couchdb; - -import org.json.JSONException; -import org.json.JSONObject; - -public class CouchDocument { - - protected final JSONObject json; - - private static final String ID = "_id"; - - private static final String DELETED = "_deleted"; - - public static CouchDocument deletedDocument(final String id) throws JSONException { - final JSONObject json = new JSONObject(); - json.put(ID, id); - json.put(DELETED, true); - return new CouchDocument(json); - } - - public CouchDocument(final JSONObject json) { - if (!json.has(ID)) { - throw new IllegalArgumentException(json + " is not a document"); - } - this.json = json; - } - - public String getId() throws JSONException { - return json.getString(ID); - } - - public boolean isDeleted() { - return json.optBoolean(DELETED, false); - } - - public JSONObject asJson() { - return json; - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/Database.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/Database.java deleted file mode 100644 index 06ff7e0dda..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/Database.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.couchdb; - -import com.github.rnewson.couchdb.lucene.util.Utils; -import org.apache.http.HttpStatus; -import org.apache.http.client.HttpClient; -import org.apache.http.client.HttpResponseException; -import org.apache.http.client.ResponseHandler; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpUriRequest; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.UUID; - -public final class Database { - - private final HttpClient httpClient; - - private final String url; - - public Database(final HttpClient httpClient, final String url) { - this.httpClient = httpClient; - this.url = url.endsWith("/") ? url : url + "/"; - } - - public boolean create() throws IOException { - return HttpUtils.put(httpClient, url, null) == 201; - } - - public boolean delete() throws IOException { - return HttpUtils.delete(httpClient, url) == 200; - } - - public List getAllDesignDocuments() throws IOException, JSONException { - final String body = HttpUtils.get(httpClient, String - .format("%s_all_docs?startkey=%s&endkey=%s&include_docs=true", - url, Utils.urlEncode("\"_design\""), Utils - .urlEncode("\"_design0\""))); - final JSONObject json = new JSONObject(body); - return toDesignDocuments(json); - } - - public CouchDocument getDocument(final String id) throws IOException, JSONException { - final String response = HttpUtils.get(httpClient, url - + Utils.urlEncode(id)); - return new CouchDocument(new JSONObject(response)); - } - - public DesignDocument getDesignDocument(final String id) throws IOException, JSONException { - final String response = HttpUtils.get(httpClient, url - + Utils.urlEncode(id)); - return new DesignDocument(new JSONObject(response)); - } - - public List getDocuments(final String... ids) - throws IOException, JSONException { - if (ids.length == 0) { - return Collections.emptyList(); - } - - final JSONArray keys = new JSONArray(); - for (final String id : ids) { - assert id != null; - keys.put(id); - } - final JSONObject req = new JSONObject(); - req.put("keys", keys); - - final String body = HttpUtils.post(httpClient, url - + "_all_docs?include_docs=true", req); - return toDocuments(new JSONObject(body)); - } - - public DatabaseInfo getInfo() throws IOException, JSONException { - return new DatabaseInfo(new JSONObject(HttpUtils.get(httpClient, - url))); - } - - public UpdateSequence getLastSequence() throws IOException, JSONException { - final JSONObject result = new JSONObject(HttpUtils.get(httpClient, url - + "_changes?limit=0&descending=true")); - return UpdateSequence.parseUpdateSequence(result.getString("last_seq")); - } - - public T handleAttachment(final String doc, final String att, - final ResponseHandler handler) throws IOException { - final HttpGet get = new HttpGet(url + "/" + Utils.urlEncode(doc) + "/" - + Utils.urlEncode(att)); - return httpClient.execute(get, handler); - } - - public HttpUriRequest getChangesRequest(final UpdateSequence since, final long timeout) - throws IOException { - final String uri; - if (timeout > -1) { - uri = url + "_changes?feed=continuous&timeout="+timeout+"&include_docs=true"; - } else { - uri = url + "_changes?feed=continuous&heartbeat=15000&include_docs=true"; - } - return new HttpGet(since.appendSince(uri)); - } - - public boolean saveDocument(final String id, final String body) - throws IOException { - return HttpUtils.put(httpClient, url + Utils.urlEncode(id), body) == 201; - } - - public UUID getUuid() throws IOException, JSONException { - try { - final CouchDocument local = getDocument("_local/lucene"); - return UUID.fromString(local.asJson().getString("uuid")); - } catch (final HttpResponseException e) { - switch (e.getStatusCode()) { - case HttpStatus.SC_NOT_FOUND: - return null; - default: - throw e; - } - } - } - - public void createUuid() throws IOException { - final UUID uuid = UUID.randomUUID(); - saveDocument("_local/lucene", String.format("{\"uuid\":\"%s\"}", uuid)); - } - - public UUID getOrCreateUuid() throws IOException, JSONException { - final UUID result = getUuid(); - if (result != null) { - return result; - } - createUuid(); - return getUuid(); - } - - private List toDesignDocuments(final JSONObject json) throws JSONException { - final List result = new ArrayList<>(); - for (final JSONObject doc : rows(json)) { - result.add(new DesignDocument(doc)); - } - return result; - } - - private List toDocuments(final JSONObject json) throws JSONException { - final List result = new ArrayList<>(); - for (final JSONObject doc : rows(json)) { - result.add(doc == null ? null : new CouchDocument(doc)); - } - return result; - } - - private List rows(final JSONObject json) throws JSONException { - final List result = new ArrayList<>(); - final JSONArray rows = json.getJSONArray("rows"); - for (int i = 0; i < rows.length(); i++) { - result.add(rows.getJSONObject(i).optJSONObject("doc")); - } - return result; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((url == null) ? 0 : url.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Database other = (Database) obj; - if (url == null) { - if (other.url != null) - return false; - } else if (!url.equals(other.url)) - return false; - return true; - } - - @Override - public String toString() { - return "Database [url=" + url + "]"; - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/DatabaseInfo.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/DatabaseInfo.java deleted file mode 100644 index cb832d2338..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/DatabaseInfo.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.couchdb; - -import org.json.JSONException; -import org.json.JSONObject; - -public class DatabaseInfo { - - private final JSONObject json; - - public DatabaseInfo(final JSONObject json) { - this.json = json; - } - - public UpdateSequence getUpdateSequence() throws JSONException { - return UpdateSequence.parseUpdateSequence(json.getString("update_seq")); - } - - public String getName() throws JSONException { - return json.getString("db_name"); - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/DesignDocument.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/DesignDocument.java deleted file mode 100644 index ba2b11deec..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/DesignDocument.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.couchdb; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -public class DesignDocument extends CouchDocument { - - private final JSONObject fulltext; - - public DesignDocument(final JSONObject json) throws JSONException { - super(json); - if (!getId().startsWith("_design/")) { - throw new IllegalArgumentException(json - + " is not a design document"); - } - fulltext = json.optJSONObject("fulltext"); - } - - public DesignDocument(final CouchDocument doc) throws JSONException { - this(doc.json); - } - - public View getView(final String name) throws JSONException { - if (fulltext == null) - return null; - final JSONObject json = fulltext.optJSONObject(name); - return json == null ? null : new View(getId() + "/" + name, json); - } - - public Map getAllViews() throws JSONException { - if (fulltext == null) - return Collections.emptyMap(); - final Map result = new HashMap<>(); - final Iterator it = fulltext.keys(); - while (it.hasNext()) { - final Object key = it.next(); - final String name = (String) key; - final View view = getView(name); - if (view != null) { - result.put(name, view); - } - } - return result; - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/FieldType.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/FieldType.java deleted file mode 100644 index c832c59411..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/FieldType.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.couchdb; - -import org.apache.commons.lang.time.DateUtils; -import org.apache.lucene.document.*; -import org.apache.lucene.index.Term; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.search.*; -import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.BytesRefBuilder; -import org.apache.lucene.util.NumericUtils; - -import java.util.Date; - -public enum FieldType { - - DATE(SortField.Type.LONG) { - @Override - public void addFields(final String name, final Object value, final ViewSettings settings, final Document to) throws ParseException { - to.add(boost(new LongPoint(name, toDate(value)), settings)); - to.add(new NumericDocValuesField(name, toDate(value))); - } - - @Override - public Query toRangeQuery(final String name, final String lower, final String upper, - final boolean lowerInclusive, final boolean upperInclusive) - throws ParseException { - return LongPoint.newRangeQuery(name, - lowerInclusive ? toDate(lower) : Math.addExact(toDate(lower), 1), - upperInclusive ? toDate(upper) : Math.addExact(toDate(upper), -1)); - } - - @Override - public Query toTermQuery(final String name, final String text) throws ParseException { - return LongPoint.newExactQuery(name, toDate(text)); - } - - }, - DOUBLE(SortField.Type.DOUBLE) { - @Override - public void addFields(final String name, final Object value, final ViewSettings settings, final Document to) { - to.add(boost(new DoublePoint(name, toDouble(value)), settings)); - to.add(new DoubleDocValuesField(name, toDouble(value))); - } - - @Override - public Query toRangeQuery(final String name, final String lower, final String upper, - final boolean lowerInclusive, final boolean upperInclusive) { - return DoublePoint.newRangeQuery(name, - lowerInclusive ? toDouble(lower) : Math.nextUp(toDouble(lower)), - upperInclusive ? toDouble(upper) : Math.nextDown(toDouble(upper))); - } - - @Override - public Query toTermQuery(final String name, final String text) { - return DoublePoint.newExactQuery(name, toDouble(text)); - } - - private double toDouble(final Object obj) { - if (obj instanceof Number) { - return ((Number) obj).doubleValue(); - } - return Double.parseDouble(obj.toString()); - } - - }, - FLOAT(SortField.Type.FLOAT) { - @Override - public void addFields(final String name, final Object value, final ViewSettings settings, final Document to) { - to.add(boost(new FloatPoint(name, toFloat(value)), settings)); - to.add(new FloatDocValuesField(name, toFloat(value))); - } - - @Override - public Query toRangeQuery(final String name, final String lower, final String upper, - final boolean lowerInclusive, final boolean upperInclusive) { - return FloatPoint.newRangeQuery(name, - lowerInclusive ? toFloat(lower) : Math.nextUp(toFloat(lower)), - upperInclusive ? toFloat(upper) : Math.nextDown(toFloat(upper))); - } - - @Override - public Query toTermQuery(final String name, final String text) { - return FloatPoint.newExactQuery(name, toFloat(text)); - } - - private float toFloat(final Object obj) { - if (obj instanceof Number) { - return ((Number) obj).floatValue(); - } - return Float.parseFloat(obj.toString()); - } - }, - INT(SortField.Type.INT) { - @Override - public void addFields(final String name, final Object value, final ViewSettings settings, final Document to) { - to.add(boost(new IntPoint(name, toInt(value)), settings)); - to.add(new NumericDocValuesField(name, toInt(value))); - } - - @Override - public Query toRangeQuery(final String name, final String lower, final String upper, - final boolean lowerInclusive, final boolean upperInclusive) { - return IntPoint.newRangeQuery(name, - lowerInclusive ? toInt(lower) : Math.addExact(toInt(lower), 1), - upperInclusive ? toInt(upper) : Math.addExact(toInt(upper), -1)); - } - - @Override - public Query toTermQuery(final String name, final String text) { - return IntPoint.newExactQuery(name, toInt(text)); - } - - private int toInt(final Object obj) { - if (obj instanceof Number) { - return ((Number) obj).intValue(); - } - return Integer.parseInt(obj.toString()); - } - - }, - LONG(SortField.Type.LONG) { - @Override - public void addFields(final String name, final Object value, final ViewSettings settings, final Document to) { - to.add(boost(new LongPoint(name, toLong(value)), settings)); - to.add(new NumericDocValuesField(name, toLong(value))); - } - - @Override - public Query toRangeQuery(final String name, final String lower, final String upper, - final boolean lowerInclusive, final boolean upperInclusive) { - return LongPoint.newRangeQuery(name, - lowerInclusive ? toLong(lower) : Math.addExact(toLong(lower), 1), - upperInclusive ? toLong(upper) : Math.addExact(toLong(upper), -1)); - } - - @Override - public Query toTermQuery(final String name, final String text) { - return LongPoint.newExactQuery(name, toLong(text)); - } - - private long toLong(final Object obj) { - if (obj instanceof Number) { - return ((Number) obj).longValue(); - } - return Long.parseLong(obj.toString()); - } - - }, - STRING(SortField.Type.STRING) { - @Override - public void addFields(final String name, final Object value, final ViewSettings settings, final Document to) { - to.add(boost(new StringField(name, value.toString(), settings.getStore()), settings)); - to.add(new SortedDocValuesField(name, new BytesRef(value.toString()))); - } - - @Override - public Query toRangeQuery(final String name, final String lower, final String upper, - final boolean lowerInclusive, final boolean upperInclusive) { - return TermRangeQuery.newStringRange(name, lower, upper, - lowerInclusive, upperInclusive); - } - - @Override - public Query toTermQuery(String name, String text) { - throw new UnsupportedOperationException("toTermQuery is not supported for FieldType.String."); - } - }, - TEXT(null) { - @Override - public void addFields(final String name, final Object value, final ViewSettings settings, final Document to) { - to.add(boost(new TextField(name, value.toString(), settings.getStore()), settings)); - } - - @Override - public Query toRangeQuery(final String name, final String lower, final String upper, - final boolean lowerInclusive, final boolean upperInclusive) { - throw new UnsupportedOperationException("toRangeQuery is not supported for TEXT"); - } - - @Override - public Query toTermQuery(String name, String text) { - throw new UnsupportedOperationException("toTermQuery is not supported for TEXT"); - } - }; - - private static T boost(final T field, final ViewSettings settings) { - field.setBoost(settings.getBoost()); - return field; - } - - public static final String[] DATE_PATTERNS = new String[]{"yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-ddZ", - "yyyy-MM-dd", "yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ss.SSS"}; - - private final SortField.Type type; - - - private FieldType(final SortField.Type type) { - this.type = type; - } - - public abstract void addFields(final String name, final Object value, final ViewSettings settings, final Document to) throws ParseException; - - public abstract Query toRangeQuery(final String name, final String lower, final String upper, - final boolean lowerInclusive, final boolean upperInclusive) - throws ParseException; - - public abstract Query toTermQuery(final String name, final String text) throws ParseException; - - public final SortField.Type toType() { - return type; - } - - public static long toDate(final Object obj) throws ParseException { - if (obj instanceof Date) { - return ((Date) obj).getTime(); - } - try { - return DateUtils.parseDate(obj.toString().toUpperCase(), DATE_PATTERNS).getTime(); - } catch (final java.text.ParseException e) { - throw new ParseException(e.getMessage()); - } - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/HttpUtils.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/HttpUtils.java deleted file mode 100644 index 863517b93f..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/HttpUtils.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.couchdb; - -import com.github.rnewson.couchdb.lucene.util.Constants; -import com.github.rnewson.couchdb.lucene.util.ErrorPreservingResponseHandler; -import com.github.rnewson.couchdb.lucene.util.StatusCodeResponseHandler; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.*; -import org.apache.http.entity.StringEntity; -import org.json.JSONObject; - -import java.io.IOException; - -public final class HttpUtils { - - private HttpUtils() { - throw new InstantiationError("This class is not supposed to be instantiated."); - } - public static final int delete(final HttpClient httpClient, final String url) throws IOException { - return httpClient.execute(new HttpDelete(url), new StatusCodeResponseHandler()); - } - - public static final String execute(final HttpClient httpClient, final HttpUriRequest request) throws IOException { - return httpClient.execute(request, new ErrorPreservingResponseHandler()); - } - - public static final String get(final HttpClient httpClient, final String url) throws IOException { - return execute(httpClient, new HttpGet(url)); - } - - public static final String post(final HttpClient httpClient, final String url, final JSONObject body) throws IOException { - final HttpPost post = new HttpPost(url); - post.setHeader("Content-Type", Constants.APPLICATION_JSON); - post.setEntity(new StringEntity(body.toString(), "UTF-8")); - return execute(httpClient, post); - } - - public static final int put(final HttpClient httpClient, final String url, final String body) throws IOException { - final HttpPut put = new HttpPut(url); - if (body != null) { - put.setHeader("Content-Type", Constants.APPLICATION_JSON); - put.setEntity(new StringEntity(body, "UTF-8")); - } - return httpClient.execute(put, new StatusCodeResponseHandler()); - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/UpdateSequence.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/UpdateSequence.java deleted file mode 100644 index e9074958fe..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/UpdateSequence.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.couchdb; - -import com.ericsson.otp.erlang.*; -import org.apache.commons.codec.binary.Base64; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * This class represents a point-in-time for a couchdb or bigcouch database. - * - * @author robertnewson - */ -public abstract class UpdateSequence { - - private static class BigCouchUpdateSequence extends UpdateSequence { - - private final String since; - private final Map vector = new HashMap<>(); - - private BigCouchUpdateSequence(final String encodedVector, final String packedSeqs) { - this.since = encodedVector; - - final byte[] bytes = new Base64(true).decode(packedSeqs); - final OtpInputStream stream = new OtpInputStream(bytes); - try { - final OtpErlangList list = (OtpErlangList) stream.read_any(); - for (int i = 0, arity = list.arity(); i < arity; i++) { - final OtpErlangTuple tuple = (OtpErlangTuple) list.elementAt(i); - final OtpErlangObject node = tuple.elementAt(0); - final OtpErlangObject range = tuple.elementAt(1); - final OtpErlangObject seq_obj = tuple.elementAt(2); - final OtpErlangLong node_seq; - if (seq_obj instanceof OtpErlangLong) { - node_seq = (OtpErlangLong) seq_obj; - } else if (seq_obj instanceof OtpErlangTuple) { - node_seq = (OtpErlangLong) ((OtpErlangTuple)seq_obj).elementAt(0); - } else { - throw new IllegalArgumentException("could not decode seq"); - } - vector.put(node + "-" + range, node_seq.longValue()); - } - } catch (final OtpErlangDecodeException e) { - throw new IllegalArgumentException(encodedVector + " not valid."); - } - } - - @Override - public String appendSince(final String url) { - try { - return url + "&since=" + URLEncoder.encode(since, "US-ASCII"); - } catch (UnsupportedEncodingException e) { - throw new Error("US-ASCII inexplicably missing."); - } - } - - @Override - public boolean isEarlierThan(final UpdateSequence other) { - if (other == START) { - return false; - } - - if (other instanceof BigCouchUpdateSequence) { - final BigCouchUpdateSequence otherBigCouch = (BigCouchUpdateSequence) other; - final Iterator> it = this.vector.entrySet().iterator(); - while (it.hasNext()) { - final Entry entry = it.next(); - final Long otherValue = otherBigCouch.vector.get(entry.getKey()); - if (otherValue != null && entry.getValue() < otherValue) { - return true; - } - } - return false; - } - - throw new IllegalArgumentException(other + " is not compatible."); - } - - @Override - public boolean isLaterThan(final UpdateSequence other) { - if (other == START) { - return true; - } - - if (other instanceof BigCouchUpdateSequence) { - final BigCouchUpdateSequence otherBigCouch = (BigCouchUpdateSequence) other; - final Iterator> it = this.vector.entrySet().iterator(); - while (it.hasNext()) { - final Entry entry = it.next(); - final Long otherValue = otherBigCouch.vector.get(entry.getKey()); - if (otherValue != null && entry.getValue() > otherValue) { - return true; - } - } - return false; - } - - throw new IllegalArgumentException(other + " is not compatible."); - } - - @Override - public String toString() { - return since; - } - } - - private static class CouchDbUpdateSequence extends UpdateSequence { - private final long seq; - - private CouchDbUpdateSequence(final String encodedIntegral) { - this.seq = Long.parseLong(encodedIntegral); - } - - @Override - public String appendSince(final String url) { - return url + "&since=" + seq; - } - - @Override - public boolean isEarlierThan(final UpdateSequence other) { - if (other == START) { - return false; - } - - if (other instanceof CouchDbUpdateSequence) { - return this.seq < ((CouchDbUpdateSequence) other).seq; - } - - throw new IllegalArgumentException(other + " is not compatible."); - } - - @Override - public boolean isLaterThan(final UpdateSequence other) { - if (other == START) { - return true; - } - - if (other instanceof CouchDbUpdateSequence) { - return this.seq > ((CouchDbUpdateSequence) other).seq; - } - - throw new IllegalArgumentException(other + " is not compatible."); - } - - @Override - public String toString() { - return Long.toString(seq); - } - - } - - private static class StartOfUpdateSequence extends UpdateSequence { - - @Override - public String appendSince(final String url) { - return url; - } - - @Override - public boolean isEarlierThan(final UpdateSequence other) { - return true; - } - - @Override - public boolean isLaterThan(final UpdateSequence other) { - return false; - } - - @Override - public String toString() { - return "start"; - } - - } - - public static final UpdateSequence START = new StartOfUpdateSequence(); - - private static Pattern BC3 = Pattern.compile("[0-9]+-([0-9a-zA-Z_-]+)"); - private static Pattern BC4 = Pattern.compile("\\[[0-9]+\\s*,\\s*\"([0-9a-zA-Z_-]+)\"\\]"); - - public static UpdateSequence parseUpdateSequence(final String str) { - if (str.matches("[0-9]+")) { - return new CouchDbUpdateSequence(str); - } - String packedSeqs; - if ((packedSeqs = extractPackedSeqs(BC3, str)) != null) { - return new BigCouchUpdateSequence(str, packedSeqs); - } - if ((packedSeqs = extractPackedSeqs(BC4, str)) != null) { - return new BigCouchUpdateSequence(str, packedSeqs); - } - throw new IllegalArgumentException(str + " not recognized."); - } - - private static String extractPackedSeqs(final Pattern p, final String str) { - final Matcher m = p.matcher(str); - if (m.matches()) { - return m.group(1); - } - return null; - } - - public abstract String appendSince(final String url); - - public abstract boolean isEarlierThan(final UpdateSequence other); - - public abstract boolean isLaterThan(final UpdateSequence other); - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/View.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/View.java deleted file mode 100644 index 54a3c29f24..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/View.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.couchdb; - -import com.github.rnewson.couchdb.lucene.util.Analyzers; -import com.github.rnewson.couchdb.lucene.util.Constants; -import org.apache.commons.lang.StringUtils; -import org.apache.lucene.analysis.Analyzer; -import org.json.JSONException; -import org.json.JSONObject; -import org.mozilla.javascript.Context; -import org.mozilla.javascript.Function; -import org.mozilla.javascript.ScriptableObject; - -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -public final class View { - - private final JSONObject json; - - private final String name; - - public View(final String name, final JSONObject json) { - if (!json.has(Constants.INDEX)) { - throw new IllegalArgumentException(json + " is not an index"); - } - this.name = name; - this.json = json; - } - - public Analyzer getAnalyzer() throws JSONException { - return Analyzers.fromSpec(json); - } - - public ViewSettings getDefaultSettings() throws JSONException { - return json.has(Constants.DEFAULTS) ? new ViewSettings(json - .getJSONObject(Constants.DEFAULTS)) : ViewSettings.getDefaultSettings(); - } - - public String getFunction() throws JSONException { - return trim(json.getString(Constants.INDEX)); - } - - public Function compileFunction(final Context context, - ScriptableObject scope) throws JSONException { - return context.compileFunction(scope, getFunction(), null, 0, null); - } - - public String getDigest() { - try { - final MessageDigest md = MessageDigest.getInstance("MD5"); - md.update(toBytes(json.optString("analyzer"))); - md.update(toBytes(json.optString("defaults"))); - md.update(toBytes(json.optString("index"))); - return new BigInteger(1, md.digest()).toString(Character.MAX_RADIX); - } catch (final NoSuchAlgorithmException e) { - throw new Error("MD5 support missing."); - } - } - - private static byte[] toBytes(final String str) { - if (str == null) { - return new byte[0]; - } - try { - return str.getBytes("UTF-8"); - } catch (final UnsupportedEncodingException e) { - throw new Error("UTF-8 support missing."); - } - } - - private static String trim(final String fun) { - String result = fun; - result = StringUtils.trim(result); - result = StringUtils.removeStart(result, "\""); - result = StringUtils.removeEnd(result, "\""); - return result; - } - - @Override - public int hashCode() { - return getDigest().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof View)) { - return false; - } - View other = (View) obj; - return getDigest().equals(other.getDigest()); - } - - @Override - public String toString() { - return String.format("View[name=%s, digest=%s]", name, getDigest()); - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/ViewSettings.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/ViewSettings.java deleted file mode 100644 index 3b134a6e61..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/couchdb/ViewSettings.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.couchdb; - -import com.github.rnewson.couchdb.lucene.util.Constants; -import org.apache.lucene.document.Field.Store; -import org.json.JSONObject; -import org.mozilla.javascript.NativeObject; - -public final class ViewSettings { - - public static ViewSettings getDefaultSettings() { - return new ViewSettings(Constants.DEFAULT_FIELD, "no", "text", "1.0", null); - } - - private final Store store; - private final String field; - private final FieldType type; - private final float boost; - - public ViewSettings(final JSONObject json) { - this(json, getDefaultSettings()); - } - - public ViewSettings(final JSONObject json, final ViewSettings defaults) { - this(json.optString("field", null), json.optString("store", null), json.optString("type", null), json.optString("boost", null), defaults); - } - - public ViewSettings(final NativeObject obj) { - this(obj, getDefaultSettings()); - } - - public ViewSettings(final NativeObject obj, final ViewSettings defaults) { - this(get(obj, "field"), get(obj, "store"), get(obj, "type"), get(obj, "boost"), defaults); - } - - private ViewSettings(final String field, final String store, final String type, final String boost, final ViewSettings defaults) { - this.field = field != null ? field : defaults.getField(); - this.store = store != null ? Store.valueOf(store.toUpperCase()) : defaults.getStore(); - this.type = type != null ? FieldType.valueOf(type.toUpperCase()) : defaults.getFieldType(); - this.boost = boost != null ? Float.valueOf(boost) : defaults.getBoost(); - } - - public float getBoost() { - return boost; - } - - public Store getStore() { - return store; - } - - public String getField() { - return field; - } - - public FieldType getFieldType() { - return type; - } - - private static String get(final NativeObject obj, final String key) { - return obj == null ? null : obj.has(key, null) ? obj.get(key, null).toString() : null; - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/rhino/JSLog.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/rhino/JSLog.java deleted file mode 100644 index a39b2123a0..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/rhino/JSLog.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.rhino; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.mozilla.javascript.ScriptableObject; - -public class JSLog extends ScriptableObject { - - private static final long serialVersionUID = 1L; - private final Logger logger = LoggerFactory.getLogger(JSLog.class); - - public JSLog() { - String[] names = {"error", "warn", "info", "debug", "trace"}; - defineFunctionProperties(names, JSLog.class, ScriptableObject.DONTENUM); - } - - @Override - public String getClassName() { - return "LogAdapter"; - } - - public void error(String mesg) { - logger.error(mesg); - } - - public void warn(String mesg) { - logger.warn(mesg); - } - - public void info(String mesg) { - logger.info(mesg); - } - - public void debug(String mesg) { - logger.debug(mesg); - } - - public void trace(String mesg) { - logger.trace(mesg); - } -} - diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/rhino/RhinoDocument.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/rhino/RhinoDocument.java deleted file mode 100644 index 0f5e3b639f..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/rhino/RhinoDocument.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.rhino; - -import com.github.rnewson.couchdb.lucene.Tika; -import com.github.rnewson.couchdb.lucene.couchdb.Database; -import com.github.rnewson.couchdb.lucene.couchdb.FieldType; -import com.github.rnewson.couchdb.lucene.couchdb.ViewSettings; -import com.github.rnewson.couchdb.lucene.util.Utils; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.ResponseHandler; -import org.apache.lucene.document.Document; -import org.apache.lucene.queryparser.classic.ParseException; -import org.mozilla.javascript.*; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -/** - * Collect data from the user. - * - * @author rnewson - */ -public final class RhinoDocument extends ScriptableObject { - - private static class RhinoAttachment { - private String attachmentName; - private String fieldName; - } - - private static class RhinoField { - private NativeObject settings; - private Object value; - } - - private static final long serialVersionUID = 1L; - - public static Scriptable jsConstructor(final Context cx, final Object[] args, final Function ctorObj, final boolean inNewExpr) { - final RhinoDocument doc = new RhinoDocument(); - if (args.length >= 2) { - jsFunction_add(cx, doc, args, ctorObj); - } - return doc; - } - - public static void jsFunction_add(final Context cx, final Scriptable thisObj, final Object[] args, final Function funObj) { - final RhinoDocument doc = checkInstance(thisObj); - - if (args.length < 1 || args.length > 2) { - throw Context.reportRuntimeError("Invalid number of arguments."); - } - - if (args[0] == null) { - // Ignore. - return; - } - - if (args[0] instanceof Undefined) { - // Ignore - return; - } - - final String className = args[0].getClass().getName(); - - if (className.equals("org.mozilla.javascript.NativeDate")) { - args[0] = (Date) Context.jsToJava(args[0], Date.class); - } - - if (!className.startsWith("java.lang.") && - !className.equals("org.mozilla.javascript.NativeObject") && - !className.equals("org.mozilla.javascript.NativeDate")) { - throw Context.reportRuntimeError(className + " is not supported."); - } - - if (args.length == 2 && (args[1] == null || args[1] instanceof NativeObject == false)) { - throw Context.reportRuntimeError("second argument must be an object."); - } - - final RhinoField field = new RhinoField(); - field.value = args[0]; - if (args.length == 2) { - field.settings = (NativeObject) args[1]; - } - - doc.fields.add(field); - } - - public static void jsFunction_attachment(final Context cx, final Scriptable thisObj, final Object[] args, final Function funObj) - throws IOException { - final RhinoDocument doc = checkInstance(thisObj); - if (args.length < 2) { - throw Context.reportRuntimeError("Invalid number of arguments."); - } - - final RhinoAttachment attachment = new RhinoAttachment(); - attachment.fieldName = args[0].toString(); - attachment.attachmentName = args[1].toString(); - doc.attachments.add(attachment); - } - - private static RhinoDocument checkInstance(final Scriptable obj) { - if (obj == null || !(obj instanceof RhinoDocument)) { - throw Context.reportRuntimeError("called on incompatible object."); - } - return (RhinoDocument) obj; - } - - private final List attachments = new ArrayList<>(); - - private final List fields = new ArrayList<>(); - - public RhinoDocument() { - } - - public Document toDocument(final String id, final ViewSettings defaults, final Database database) throws IOException, - ParseException { - final Document result = new Document(); - - // Add id. - result.add(Utils.token("_id", id, true)); - - // Add user-supplied fields. - for (final RhinoField field : fields) { - addField(field, defaults, result); - } - - // Parse user-requested attachments. - for (final RhinoAttachment attachment : attachments) { - addAttachment(attachment, id, database, result); - } - - return result; - } - - @Override - public String getClassName() { - return "Document"; - } - - private void addAttachment(final RhinoAttachment attachment, final String id, final Database database, final Document out) - throws IOException { - final ResponseHandler handler = new ResponseHandler() { - - public Void handleResponse(final HttpResponse response) throws ClientProtocolException, IOException { - final HttpEntity entity = response.getEntity(); - try { - Tika.INSTANCE.parse(entity.getContent(), entity.getContentType().getValue(), attachment.fieldName, out); - } finally { - entity.consumeContent(); - } - return null; - } - }; - - database.handleAttachment(id, attachment.attachmentName, handler); - } - - private void addField(final RhinoField field, final ViewSettings defaults, final Document out) throws ParseException { - final ViewSettings settings = new ViewSettings(field.settings, defaults); - final FieldType type = settings.getFieldType(); - type.addFields(settings.getField(), field.value, settings, out); - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/Analyzers.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/Analyzers.java deleted file mode 100644 index f54b6cbe38..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/Analyzers.java +++ /dev/null @@ -1,544 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.util; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.AnalyzerWrapper; -import org.apache.lucene.analysis.CharArraySet; -import org.apache.lucene.analysis.Tokenizer; -import org.apache.lucene.analysis.br.BrazilianAnalyzer; -import org.apache.lucene.analysis.cjk.CJKAnalyzer; -import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer; -import org.apache.lucene.analysis.core.KeywordAnalyzer; -import org.apache.lucene.analysis.core.LowerCaseTokenizer; -import org.apache.lucene.analysis.core.SimpleAnalyzer; -import org.apache.lucene.analysis.core.WhitespaceAnalyzer; -import org.apache.lucene.analysis.cz.CzechAnalyzer; -import org.apache.lucene.analysis.de.GermanAnalyzer; -import org.apache.lucene.analysis.fr.FrenchAnalyzer; -import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper; -import org.apache.lucene.analysis.nl.DutchAnalyzer; -import org.apache.lucene.analysis.ru.RussianAnalyzer; -import org.apache.lucene.analysis.standard.ClassicAnalyzer; -import org.apache.lucene.analysis.standard.StandardAnalyzer; -import org.apache.lucene.analysis.th.ThaiAnalyzer; -import org.apache.lucene.analysis.ngram.NGramTokenFilter; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.Reader; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public enum Analyzers { - - BRAZILIAN { - @Override - public Analyzer newAnalyzer(final String args) { - return new BrazilianAnalyzer(); - } - @Override - public Analyzer newAnalyzer(final JSONObject args) { - return new BrazilianAnalyzer(); - } - }, - CHINESE { - @Override - public Analyzer newAnalyzer(final String args) { - return new SmartChineseAnalyzer(); - } - @Override - public Analyzer newAnalyzer(final JSONObject args) { - return new SmartChineseAnalyzer(); - } - }, - CJK { - @Override - public Analyzer newAnalyzer(final String args) { - return new CJKAnalyzer(); - } - @Override - public Analyzer newAnalyzer(final JSONObject args) { - return new CJKAnalyzer(); - } - }, - CLASSIC { - @Override - public Analyzer newAnalyzer(final String args) { - return new ClassicAnalyzer(); - } - @Override - public Analyzer newAnalyzer(final JSONObject args) { - return new ClassicAnalyzer(); - } - }, - CZECH { - @Override - public Analyzer newAnalyzer(final String args) { - return new CzechAnalyzer(); - } - @Override - public Analyzer newAnalyzer(final JSONObject args) { - return new CzechAnalyzer(); - } - }, - DUTCH { - @Override - public Analyzer newAnalyzer(final String args) { - return new DutchAnalyzer(); - } - @Override - public Analyzer newAnalyzer(final JSONObject args) { - return new DutchAnalyzer(); - } - }, - ENGLISH { - @Override - public Analyzer newAnalyzer(final String args) { - return new StandardAnalyzer(); - } - @Override - public Analyzer newAnalyzer(final JSONObject args) { - return new StandardAnalyzer(); - } - }, - FRENCH { - @Override - public Analyzer newAnalyzer(final String args) { - return new FrenchAnalyzer(); - } - @Override - public Analyzer newAnalyzer(final JSONObject args) { - return new FrenchAnalyzer(); - } - }, - GERMAN { - @Override - public Analyzer newAnalyzer(final String args) { - return new GermanAnalyzer(); - } - @Override - public Analyzer newAnalyzer(final JSONObject args) { - return new GermanAnalyzer(); - } - }, - KEYWORD { - @Override - public Analyzer newAnalyzer(final String args) { - return new KeywordAnalyzer(); - } - @Override - public Analyzer newAnalyzer(final JSONObject args) { - return new KeywordAnalyzer(); - } - }, - PERFIELD { - @Override - public Analyzer newAnalyzer(final String args) throws JSONException { - final JSONObject json = new JSONObject(args == null ? "{}" : args); - return PERFIELD.newAnalyzer(json); - } - @Override - public Analyzer newAnalyzer(final JSONObject json) throws JSONException { - final Analyzer defaultAnalyzer = fromSpec(json, Constants.DEFAULT_FIELD); - final Map analyzers = new HashMap<>(); - final Iterator it = json.keys(); - while (it.hasNext()) { - final String key = it.next().toString(); - if (Constants.DEFAULT_FIELD.equals(key)) - continue; - analyzers.put(key, fromSpec(json, key)); - } - return new PerFieldAnalyzerWrapper(defaultAnalyzer, analyzers); - } - }, - RUSSIAN { - @Override - public Analyzer newAnalyzer(final String args) { - return new RussianAnalyzer(); - } - @Override - public Analyzer newAnalyzer(final JSONObject args) { - return new RussianAnalyzer(); - } - }, - SIMPLE { - @Override - public Analyzer newAnalyzer(final String args) { - return new SimpleAnalyzer(); - } - @Override - public Analyzer newAnalyzer(final JSONObject args) { - return new SimpleAnalyzer(); - } - }, - STANDARD { - @Override - public Analyzer newAnalyzer(final String args) { - return new StandardAnalyzer(); - } - @Override - public Analyzer newAnalyzer(final JSONObject args) { - return new StandardAnalyzer(); - } - }, - THAI { - @Override - public Analyzer newAnalyzer(final String args) { - return new ThaiAnalyzer(); - } - @Override - public Analyzer newAnalyzer(final JSONObject args) { - return new ThaiAnalyzer(); - } - }, - WHITESPACE { - public Analyzer newAnalyzer(final String args) { - return new WhitespaceAnalyzer(); - } - @Override - public Analyzer newAnalyzer(final JSONObject args) { - return new WhitespaceAnalyzer(); - } - }, - NGRAM { - public Analyzer newAnalyzer(final String args) throws JSONException { - final JSONObject json = new JSONObject(args == null ? "{}" : args); - return NGRAM.newAnalyzer(json); - } - @Override - public Analyzer newAnalyzer(final JSONObject json) throws JSONException { - Analyzer analyzer = fromSpec(json); - int min = json.optInt("min", NGramTokenFilter.DEFAULT_MIN_NGRAM_SIZE); - int max = json.optInt("max", NGramTokenFilter.DEFAULT_MAX_NGRAM_SIZE); - return new NGramAnalyzer(analyzer, min, max); - } - }; - - private static final class NGramAnalyzer extends AnalyzerWrapper { - private final Analyzer analyzer; - private final int min; - private final int max; - - public NGramAnalyzer(final Analyzer analyzer, final int min, final int max) { - super(Analyzer.GLOBAL_REUSE_STRATEGY); - this.analyzer = analyzer; - this.min = min; - this.max = max; - } - - @Override - protected Analyzer getWrappedAnalyzer(final String fieldName) { - return analyzer; - } - - @Override - protected TokenStreamComponents wrapComponents(String fieldName, TokenStreamComponents components) { - return new TokenStreamComponents(components.getTokenizer(), - new NGramTokenFilter(components.getTokenStream(), - this.min, this.max)); - } - } - - public static Analyzer fromSpec(final JSONObject json, final String analyzerKey) throws JSONException { - JSONObject spec = json.optJSONObject(analyzerKey); - if (spec != null) { - return getAnalyzer(spec); - } else { - return getAnalyzer(json.optString(analyzerKey, Constants.DEFAULT_ANALYZER)); - } - } - - public static Analyzer fromSpec(final JSONObject json) throws JSONException { - return fromSpec(json, Constants.ANALYZER); - } - - /* - * called from DatabaseIndexer when handling an http search request - */ - public static Analyzer fromSpec(String str) throws JSONException { - if (str == null) { - return getAnalyzer(Constants.DEFAULT_ANALYZER); - } - - if (str.startsWith("{")) { - try { - return getAnalyzer(new JSONObject(str)); - - } catch (JSONException ex) { - logger.error("Analyzer spec is not well-formed json. Using default analyzer!", ex); - return getAnalyzer(Constants.DEFAULT_ANALYZER); - } - } - - return getAnalyzer(str); - } - - public static Analyzer getAnalyzer(final String str) throws JSONException { - final String[] parts = str.split(":", 2); - final String name = parts[0].toUpperCase(); - final String args = parts.length == 2 ? parts[1] : null; - return Analyzers.valueOf(name).newAnalyzer(args); - } - - public static Analyzer getAnalyzer(final JSONObject json) throws JSONException { - String className = json.optString(Constants.CLASS); - JSONArray params = json.optJSONArray(Constants.PARAMS); - - if (className == null || className.isEmpty()) { - Iterator it = json.keys(); - if (it.hasNext()) { - String key = (String) it.next(); - String args = json.optString(key); - - JSONObject obj = json.optJSONObject(key); - if (obj != null) { - return Analyzers.valueOf(key.toUpperCase()).newAnalyzer(obj); - } else { - return Analyzers.valueOf(key.toUpperCase()).newAnalyzer(args); - } - } - - logger.error("No analyzer class name defined in " + json); - return null; - } - - // is the class accessible? - Class clazz = null; - try { - clazz = Class.forName(className); - } catch (ClassNotFoundException e) { - logger.error("Analyzer class " + className + " not found. " + e.getMessage(), e); - return null; - } - - // Is the class an Analyzer? - if (!Analyzer.class.isAssignableFrom(clazz)) { - logger.error(clazz.getName() + " has to be a subclass of " + Analyzer.class.getName()); - return null; - } - - // Get list of parameters - List paramSpecs; - try { - paramSpecs = getParamSpecs(params); - } catch (ParameterException | JSONException ex) { - logger.error("Unable to parse parameter specs for " + className + ". " + ex.getMessage(), ex); - return null; - } - - // split param specs into classes and values for constructor lookup - final Class paramClasses[] = new Class[paramSpecs.size()]; - final Object paramValues[] = new Object[paramSpecs.size()]; - for (int i = 0; i < paramSpecs.size(); i++) { - ParamSpec spec = paramSpecs.get(i); - paramClasses[i] = spec.getValueClass(); - paramValues[i] = spec.getValue(); - } - - // Create new analyzer - return newAnalyzer(clazz, paramClasses, paramValues); - } - - /** - * Create instance of the lucene analyzer with provided arguments - * - * @param clazz The analyzer class - * @param paramClasses The parameter classes - * @param paramValues The parameter values - * @return The lucene analyzer - */ - private static Analyzer newAnalyzer(Class clazz, Class[] paramClasses, Object[] paramValues) { - - String className = clazz.getName(); - - try { - final Constructor cstr = clazz.getDeclaredConstructor(paramClasses); - - return (Analyzer) cstr.newInstance(paramValues); - - } catch (IllegalArgumentException | IllegalAccessException | InstantiationException | InvocationTargetException | SecurityException e) { - logger.error("Exception while instantiating analyzer class " + className + ". " + e.getMessage(), e); - } catch (NoSuchMethodException ex) { - logger.error("Could not find matching analyzer class constructor for " + className + " " + ex.getMessage(), ex); - } - - return null; - } - - /** - * Retrieve the list of parameter specs for the analyzer - */ - private static List getParamSpecs(JSONArray jsonParams) throws ParameterException, JSONException { - final List paramSpecs = new ArrayList<>(); - - if (jsonParams != null) { - for (int i = 0; i < jsonParams.length(); i++) { - paramSpecs.add(getParamSpec(jsonParams.getJSONObject(i))); - } - } - - return paramSpecs; - } - - /** - * Parse an analyzer constructor parameter spec. - * - * Each param spec looks like: - * - *
{ "name": <a name>, "type": <oneof: set, bool, int, file, string>, "value": <value> }
- * - * The name serves to document the purpose of the parameter. Values of type set are JSON arrays and - * are used to represent lucene CharArraySets such as for stop words in StandardAnalyzer - * - * @param param json object specifying an analyzer parameter - * @return ParamSpec - * @throws ParameterException - * @throws JSONException - */ - private static ParamSpec getParamSpec(JSONObject param) throws ParameterException, JSONException { - - final String name = param.optString("name"); - final String type = param.optString("type", "string"); - final String value = param.optString("value"); - - switch (type) { - - // String - case "string": { - if (value == null) { - throw new ParameterException("Value for string param: " + name + " is not empty!"); - } - - return new ParamSpec(name, value, String.class); - } - // "java.io.FileReader": - case "file": { - - if (value == null) { - throw new ParameterException("The 'value' field of a file param must exist and must contain a file name."); - } - - try { - // The analyzer is responsible for closing the file - Reader fileReader = new java.io.FileReader(value); - return new ParamSpec(name, fileReader, Reader.class); - - } catch (java.io.FileNotFoundException ex) { - throw new ParameterException("File " + value + " for param " + name + " not found!"); - } - } - // "org.apache.lucene.analysis.util.CharArraySet": - case "set": { - JSONArray values = param.optJSONArray("value"); - - if (values == null) { - throw new ParameterException("The 'value' field of a set param must exist and must contain a json array of strings."); - } - - final Set set = new HashSet<>(); - - for (int i = 0; i < values.length(); i++) { - set.add(values.getString(i)); - } - - return new ParamSpec(name, CharArraySet.copy(set), CharArraySet.class); - } - // "int": - case "int": - - int n = param.optInt("value"); - return new ParamSpec(name, n, int.class); - - // "boolean": - case "boolean": - - boolean b = param.optBoolean("value"); - return new ParamSpec(name, b, boolean.class); - - default: - // there was no match - logger.error("Unknown parameter type: " + type + " for param: " + name + " with value: " + value); - break; - } - - return null; - } - - /** - * CLass for containing the Triple : key (name), corresponding value and - * class type of value. - */ - private static final class ParamSpec { - - private final String key; - private final Object value; - private final Class clazz; - - @SuppressWarnings("unused") - public ParamSpec(String key, Object value) { - this(key, value, value.getClass()); - } - - public ParamSpec(String key, Object value, Class clazz) { - this.key = key; - this.value = value; - this.clazz = clazz; - } - - @SuppressWarnings("unused") - public String getKey() { - return key; - } - - public Object getValue() { - return value; - } - - public Class getValueClass() { - return clazz; - } - } - - /** - * Exception class to for reporting problems with the parameters. - */ - @SuppressWarnings("serial") - private static class ParameterException extends Exception { - - public ParameterException(String message) { - super(message); - } - } - - public abstract Analyzer newAnalyzer(final String args) throws JSONException; - - public abstract Analyzer newAnalyzer(final JSONObject args) throws JSONException; - - static Logger logger = LoggerFactory.getLogger(Analyzers.class.getName()); - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/Constants.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/Constants.java deleted file mode 100644 index 36e5132ff1..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/Constants.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.util; - -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.standard.StandardAnalyzer; -import org.apache.lucene.util.Version; - -public final class Constants { - - private Constants() { - throw new InstantiationError("This class is not supposed to be instantiated."); - } - - public static final String APPLICATION_JSON = "application/json"; - - public static final String DEFAULT_FIELD = "default"; - - public static final String DEFAULT_ANALYZER = "standard"; - public static final String ANALYZER = "analyzer"; - public static final String INDEX = "index"; - public static final String DEFAULTS = "defaults"; - public static final String CLASS = "class"; - public static final String PARAMS = "params"; - public static final String TYPE = "type"; - public static final String VALUE = "value"; -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/ErrorPreservingResponseHandler.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/ErrorPreservingResponseHandler.java deleted file mode 100644 index bab498ee8d..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/ErrorPreservingResponseHandler.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.util; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.StatusLine; -import org.apache.http.client.HttpResponseException; -import org.apache.http.client.ResponseHandler; -import org.apache.http.util.EntityUtils; - -import java.io.IOException; - -public final class ErrorPreservingResponseHandler implements ResponseHandler { - - public String handleResponse(final HttpResponse response) throws HttpResponseException, IOException { - final StatusLine statusLine = response.getStatusLine(); - final HttpEntity entity = response.getEntity(); - final String str = entity == null ? null : EntityUtils.toString(entity, "UTF-8"); - - if (statusLine.getStatusCode() >= 300) { - throw new HttpResponseException(statusLine.getStatusCode(), str); - } - - return str; - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/ServletUtils.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/ServletUtils.java deleted file mode 100644 index 552829966f..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/ServletUtils.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.util; - -import org.json.JSONException; -import org.json.JSONObject; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.Writer; - -public final class ServletUtils { - - private ServletUtils() { - throw new InstantiationError("This class is not supposed to be instantiated."); - } - - public static boolean getBooleanParameter(final HttpServletRequest req, final String parameterName) { - return Boolean.parseBoolean(req.getParameter(parameterName)); - } - - public static int getIntParameter(final HttpServletRequest req, final String parameterName, final int defaultValue) { - final String result = req.getParameter(parameterName); - return result != null ? Integer.parseInt(result) : defaultValue; - } - - public static long getLongParameter(final HttpServletRequest req, final String parameterName, final long defaultValue) { - final String result = req.getParameter(parameterName); - return result != null ? Long.parseLong(result) : defaultValue; - } - - public static String getParameter(final HttpServletRequest req, final String parameterName, final String defaultValue) { - final String result = req.getParameter(parameterName); - return result != null ? result : defaultValue; - } - - public static void setResponseContentTypeAndEncoding(final HttpServletRequest req, final HttpServletResponse resp) { - final String accept = req.getHeader("Accept"); - if (getBooleanParameter(req, "force_json") || (accept != null && accept.contains("application/json"))) { - resp.setContentType("application/json"); - } else { - resp.setContentType("text/plain"); - } - if (!resp.containsHeader("Vary")) { - resp.addHeader("Vary", "Accept"); - } - resp.setCharacterEncoding("utf-8"); - } - - public static void sendJsonError(final HttpServletRequest request, final HttpServletResponse response, final int code, - final String reason) throws IOException, JSONException { - final JSONObject obj = new JSONObject(); - obj.put("reason", reason); - sendJsonError(request, response, code, obj); - } - - public static void sendJsonError(final HttpServletRequest request, final HttpServletResponse response, final int code, - final JSONObject error) throws IOException, JSONException { - setResponseContentTypeAndEncoding(request, response); - response.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); - response.setStatus(code); - error.put("code", code); - - final Writer writer = response.getWriter(); - try { - writer.write(error.toString()); - writer.write("\r\n"); - } finally { - writer.close(); - } - } - - public static void sendJson(final HttpServletRequest req, final HttpServletResponse resp, final JSONObject json) throws IOException { - setResponseContentTypeAndEncoding(req, resp); - final Writer writer = resp.getWriter(); - try { - writer.write(json.toString() + "\r\n"); - } finally { - writer.close(); - } - } - - public static void sendJsonSuccess(final HttpServletRequest req, final HttpServletResponse resp) throws IOException { - setResponseContentTypeAndEncoding(req, resp); - final Writer writer = resp.getWriter(); - try { - writer.write("{\"ok\": true}\r\n"); - } finally { - writer.close(); - } - } - - //Strip of context part of URI - public static String getUri(HttpServletRequest request) { - //Strip of context path if present - return request.getRequestURI().substring(request.getContextPath().length()); - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/StatusCodeResponseHandler.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/StatusCodeResponseHandler.java deleted file mode 100644 index 188d8571f7..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/StatusCodeResponseHandler.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.util; - -import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.ResponseHandler; - -import java.io.IOException; - -/** - * Just return the status code (mostly used for PUT calls). - * - * @author rnewson - */ -public final class StatusCodeResponseHandler implements ResponseHandler { - - public Integer handleResponse(final HttpResponse response) throws ClientProtocolException, IOException { - return response.getStatusLine().getStatusCode(); - } - -} \ No newline at end of file diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/StopWatch.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/StopWatch.java deleted file mode 100644 index f7b35d126a..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/StopWatch.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.util; - -import java.util.HashMap; -import java.util.Map; - -public final class StopWatch { - - private final Map elapsed = new HashMap<>(); - - private long start = System.nanoTime(); - - public long getElapsed(final String name) { - return elapsed.get(name) / 1000000; - } - - public void lap(final String name) { - final long now = System.nanoTime(); - elapsed.put(name, now - start); - start = now; - } - -} diff --git a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/Utils.java b/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/Utils.java deleted file mode 100644 index 6f25bd634c..0000000000 --- a/third-party/couchdb-lucene/src/main/java/com/github/rnewson/couchdb/lucene/util/Utils.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.util; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.apache.lucene.document.Field; -import org.apache.lucene.document.StringField; -import org.apache.lucene.document.TextField; -import org.apache.lucene.document.Field.Store; -import org.apache.lucene.store.Directory; - -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; - -public final class Utils { - - private Utils() { - throw new InstantiationError("This class is not supposed to be instantiated."); - } - - public static Logger getLogger(final Class clazz, final String suffix) { - return LoggerFactory.getLogger(clazz.getCanonicalName() + "." + suffix); - } - - public static boolean getStaleOk(final HttpServletRequest req) { - return "ok".equals(req.getParameter("stale")); - } - - public static Field text(final String name, final String value, final boolean store) { - return new TextField(name, value, store ? Store.YES : Store.NO); - } - - public static Field token(final String name, final String value, final boolean store) { - return new StringField(name, value, store ? Store.YES : Store.NO); - } - - public static String urlEncode(final String path) { - try { - return URLEncoder.encode(path, "UTF-8"); - } catch (final UnsupportedEncodingException e) { - throw new Error("UTF-8 support missing!"); - } - } - - public static long directorySize(final Directory dir) throws IOException { - long result = 0; - for (final String name : dir.listAll()) { - result += dir.fileLength(name); - } - return result; - } - - /** - * Split a string on commas but respect commas inside quotes. - * - * @param str - * @return - */ - public static String[] splitOnCommas(final String str) { - return str.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"); - } - -} diff --git a/third-party/couchdb-lucene/src/main/resources/couchdb-lucene.ini b/third-party/couchdb-lucene/src/main/resources/couchdb-lucene.ini deleted file mode 100644 index 4e44d09ab9..0000000000 --- a/third-party/couchdb-lucene/src/main/resources/couchdb-lucene.ini +++ /dev/null @@ -1,26 +0,0 @@ -[lucene] -# The output directory for Lucene indexes. -dir=indexes - -# The local host name that couchdb-lucene binds to -host=localhost - -# The port that couchdb-lucene binds to. -port=5985 - -# Timeout for requests in milliseconds. -timeout=10000 - -# Timeout for changes requests. -# changes_timeout=60000 - -# Default limit for search results -limit=25 - -# Allow leading wildcard? -allowLeadingWildcard=false - -# couchdb server mappings - -[local] -url = http://localhost:5984/ diff --git a/third-party/couchdb-lucene/src/main/tools/etc/init.d/couchdb-lucene b/third-party/couchdb-lucene/src/main/tools/etc/init.d/couchdb-lucene deleted file mode 100644 index 69b05c14f3..0000000000 --- a/third-party/couchdb-lucene/src/main/tools/etc/init.d/couchdb-lucene +++ /dev/null @@ -1,160 +0,0 @@ -#! /bin/sh -### BEGIN INIT INFO -# Provides: couchdb-lucene -# Required-Start: $remote_fs $syslog -# Required-Stop: $remote_fs $syslog -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Initscript for CouchDB-Lucene -# Description: Initscript for CouchDB-Lucene -### END INIT INFO - -# Author: Sebastian Cohnen -# - -# Do NOT "set -e" - -PATH=/sbin:/usr/sbin:/bin:/usr/bin -DESC="Description of the service" -NAME="couchdb-lucene" -DAEMON=/usr/local/couchdb-lucene-0.8.0/bin/run -PIDFILE=/var/run/$NAME.pid -DAEMON_ARGS="$PIDFILE" -SCRIPTNAME=/etc/init.d/$NAME - -# Exit if the package is not installed -[ -x "$DAEMON" ] || exit 0 - -# Read configuration variable file if it is present -[ -r /etc/default/$NAME ] && . /etc/default/$NAME - -# Load the VERBOSE setting and other rcS variables -. /lib/init/vars.sh - -# Define LSB log_* functions. -# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. -. /lib/lsb/init-functions - -# -# Function that starts the daemon/service -# -do_start() -{ - # Return - # 0 if daemon has been started - # 1 if daemon was already running - # 2 if daemon could not be started - PIDFILE=$PIDFILE start-stop-daemon --start --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ - || return 1 - PIDFILE=$PIDFILE start-stop-daemon --start --pidfile $PIDFILE --exec $DAEMON -- \ - $DAEMON_ARGS \ - || return 2 - # Add code here, if necessary, that waits for the process to be ready - # to handle requests from services started subsequently which depend - # on this one. As a last resort, sleep for some time. -} - -# -# Function that stops the daemon/service -# -do_stop() -{ - # Return - # 0 if daemon has been stopped - # 1 if daemon was already stopped - # 2 if daemon could not be stopped - # other if a failure occurred - start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE - RETVAL="$?" - [ "$RETVAL" = 2 ] && return 2 - # Wait for children to finish too if this is a daemon that forks - # and if the daemon is only ever run from this initscript. - # If the above conditions are not satisfied then add some other code - # that waits for the process to drop all resources that could be - # needed by services started subsequently. A last resort is to - # sleep for some time. - start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON - [ "$?" = 2 ] && return 2 - # Many daemons don't delete their pidfiles when they exit. - rm -f $PIDFILE - return "$RETVAL" -} - -# -# Function that sends a SIGHUP to the daemon/service -# -do_reload() { - # - # If the daemon can reload its configuration without - # restarting (for example, when it is sent a SIGHUP), - # then implement that here. - # - start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME - return 0 -} - -case "$1" in - start) - [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" - do_start - case "$?" in - 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; - 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; - esac - ;; - stop) - [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" - do_stop - case "$?" in - 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; - 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; - esac - ;; - status) - #status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? - if [ ! -f $PIDFILE -o ! -d /proc/`cat $PIDFILE` ] - then - log_failure_msg "$NAME is not running" - else - log_success_msg "$NAME is running" - fi - ;; - #reload|force-reload) - # - # If do_reload() is not implemented then leave this commented out - # and leave 'force-reload' as an alias for 'restart'. - # - #log_daemon_msg "Reloading $DESC" "$NAME" - #do_reload - #log_end_msg $? - #;; - restart|force-reload) - # - # If the "reload" option is implemented then remove the - # 'force-reload' alias - # - log_daemon_msg "Restarting $DESC" "$NAME" - do_stop - case "$?" in - 0|1) - do_start - case "$?" in - 0) log_end_msg 0 ;; - 1) log_end_msg 1 ;; # Old process is still running - *) log_end_msg 1 ;; # Failed to start - esac - ;; - *) - # Failed to stop - log_end_msg 1 - ;; - esac - ;; - *) - #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 - echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 - exit 3 - ;; -esac - -: diff --git a/third-party/couchdb-lucene/src/main/webapp/WEB-INF/web.xml b/third-party/couchdb-lucene/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 1a648f6237..0000000000 --- a/third-party/couchdb-lucene/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - couchdb-lucene - Enables full-text searching of CouchDB documents using Lucene - - - - lucene - com.github.rnewson.couchdb.lucene.LuceneServlet - - - - - lucene - / - - - diff --git a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/ConfigTest.java b/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/ConfigTest.java deleted file mode 100644 index dd19729dfe..0000000000 --- a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/ConfigTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import org.apache.commons.configuration.ConfigurationException; -import org.apache.commons.configuration.HierarchicalINIConfiguration; -import org.apache.http.client.HttpClient; -import org.junit.Test; - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; - -import static org.junit.Assert.*; - -public class ConfigTest { - - @Test - public void testGetConfiguration() { - try { - final Config config = new Config(); - HierarchicalINIConfiguration configuration = config - .getConfiguration(); - assertEquals("localhost", configuration.getString("lucene.host")); - assertEquals(5985, configuration.getInt("lucene.port")); - } catch (ConfigurationException ce) { - fail("ConfigurationException shouldn't have been thrown." - + ce.getMessage()); - } - } - - @Test - public void testGetDir() { - try { - final Config config = new Config(); - File dir = config.getDir(); - assertTrue(dir.exists()); - assertTrue(dir.canRead()); - assertTrue(dir.canWrite()); - assertEquals(new File("target", "indexes"), dir); - } catch (ConfigurationException ce) { - fail("ConfigurationException shouldn't have been thrown." - + ce.getMessage()); - } catch (IOException ioe) { - fail("IOException shouldn't have been thrown." + ioe.getMessage()); - } - } - - @Test - public void testGetClient() { - try { - final Config config = new Config(); - HttpClient client = config.getClient(); - assertNotNull(client); - } catch (ConfigurationException ce) { - fail("ConfigurationException shouldn't have been thrown." - + ce.getMessage()); - } catch (MalformedURLException mue) { - fail("MalformedURLException shouldn't have been thrown." - + mue.getMessage()); - } - } -} diff --git a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/CustomQueryParserTest.java b/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/CustomQueryParserTest.java deleted file mode 100644 index 8b3c62b0b8..0000000000 --- a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/CustomQueryParserTest.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import com.github.rnewson.couchdb.lucene.couchdb.FieldType; -import com.github.rnewson.couchdb.lucene.util.Constants; -import org.apache.lucene.analysis.standard.StandardAnalyzer; -import org.apache.lucene.queryparser.classic.ParseException; -import org.apache.lucene.search.PointRangeQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermQuery; -import org.junit.Before; -import org.junit.Test; - -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; - -public class CustomQueryParserTest { - - private CustomQueryParser parser; - - @Before - public void setup() { - parser = new CustomQueryParser("default", new StandardAnalyzer()); - } - - @Test - public void integerRangeQuery() throws Exception { - final Query q = parser.parse("blah:[0 TO 123]"); - assertRange(q, Integer.class, 0, 123); - } - - @Test - public void longRangeQuery() throws Exception { - final Query q = parser.parse("blah:[0 TO 123]"); - assertRange(q, Long.class, 0L, 123L); - } - - @Test - public void floatRangeQuery() throws Exception { - final Query q = parser.parse("blah:[0.0 TO 123.5]"); - assertRange(q, Float.class, 0.0f, 123.5f); - } - - @Test - public void doubleRangeQuery() throws Exception { - final Query q = parser.parse("blah:[0.0 TO 123.0]"); - assertRange(q, Double.class, 0.0, 123.0); - } - - @Test - public void dateRangeQuery() throws Exception { - final Query q = parser.parse("blah:[2000-01-01 TO 2010-02-04]"); - assertRange(q, Long.class, time("2000-01-01"), time("2010-02-04")); - } - - @Test - public void dateTimeRangeQuery() throws Exception { - final Query q = parser.parse("blah:[2000-01-01T00:00:01 TO 2010-02-04T00:00:01]"); - assertRange(q, Long.class, time("2000-01-01T00:00:01"), time("2010-02-04T00:00:01")); - } - - @Test - public void dateTimeZoneRangeQuery() throws Exception { - final Query q = parser.parse("blah:[2000-01-01-0100 TO 2010-02-04-0100]"); - assertRange(q, Long.class, time("2000-01-01-0100"), time("2010-02-04-0100")); - } - - @Test - public void dateTimeTimeZoneRangeQuery() throws Exception { - final Query q = parser.parse("blah:[2000-01-01T00:00:00-0100 TO 2010-02-04T00:00:00-0100]"); - assertRange(q, Long.class, time("2000-01-01T00:00:00-0100"), time("2010-02-04T00:00:00-0100")); - } - - @Test - public void fieldNameWithDashes() throws Exception { - final Query q = parser.parse("foo-bar:baz"); - assertThat(q, is(TermQuery.class)); - } - - @Test - public void fieldNameWithEscapedSpaces() throws Exception { - final Query q = parser.parse("foo\\ bar:baz"); - assertThat(q, is(TermQuery.class)); - } - - @Test - public void fieldNameWithNonAscii() throws Exception { - final Query q = parser.parse("foó:bar"); - assertThat(q, is(TermQuery.class)); - } - - private long time(final String str) throws ParseException { - return FieldType.toDate(str); - } - - private void assertRange(final Query q, final Class type, final Number min, final Number max) { - assertThat(q, is(PointRangeQuery.class)); - } - -} diff --git a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/DocumentConverterTest.java b/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/DocumentConverterTest.java deleted file mode 100644 index 0861af8f35..0000000000 --- a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/DocumentConverterTest.java +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import com.github.rnewson.couchdb.lucene.couchdb.CouchDocument; -import com.github.rnewson.couchdb.lucene.couchdb.View; -import com.github.rnewson.couchdb.lucene.couchdb.ViewSettings; -import com.github.rnewson.couchdb.lucene.util.Constants; -import org.apache.lucene.document.Document; -import org.apache.lucene.document.IntPoint; -import org.apache.lucene.document.LongPoint; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.json.JSONException; -import org.json.JSONObject; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mozilla.javascript.Context; -import org.mozilla.javascript.EvaluatorException; - -import java.util.Collection; -import java.util.TimeZone; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.Matchers.hasItemInArray; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.assertThat; - -public class DocumentConverterTest { - - private Context context; - - private TimeZone tz; - private static final Logger LOG = LoggerFactory.getLogger(DocumentConverterTest.class); - - @Before - public void setup() { - context = Context.enter(); - tz = TimeZone.getDefault(); - TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); - } - - @After - public void teardown() { - TimeZone.setDefault(tz); - Context.exit(); - } - - @Test - public void testSingleDocumentReturn() throws Exception { - final DocumentConverter converter = new DocumentConverter( - context, - view("function(doc) {return new Document();}")); - final Collection result = converter.convert(doc("{_id:\"hello\"}"), settings(), null); - assertThat(result.size(), is(1)); - assertThat(result.iterator().next().get("_id"), is("hello")); - } - - @Test - public void testMultipleDocumentReturn() throws Exception { - final DocumentConverter converter = new DocumentConverter( - context, - view("function(doc) {var ret = new Array(); ret.push(new Document()); ret.push(new Document()); return ret;}")); - final Collection result = converter.convert(doc("{_id:\"hello\"}"), settings(), null); - assertThat(result.size(), is(2)); - assertThat(result.iterator().next().get("_id"), is("hello")); - } - - @Test - public void testAdd() throws Exception { - final DocumentConverter converter = new DocumentConverter( - context, - view("function(doc) {var ret=new Document(); ret.add(doc.key); return ret;}")); - final Collection result = converter.convert( - doc("{_id:\"hello\", key:\"value\"}"), - settings(), - null); - assertThat(result.size(), is(1)); - assertThat(result.iterator().next().get(Constants.DEFAULT_FIELD), is("value")); - } - - @Test - public void testForLoopOverObject() throws Exception { - final DocumentConverter converter = new DocumentConverter( - context, - view("function(doc) {var ret=new Document(); for (var key in doc) { ret.add(doc[key]); } return ret; }")); - final Collection result = converter.convert( - doc("{_id:\"hello\", key:\"value\"}"), - settings(), - null); - assertThat(result.size(), is(1)); - assertThat(result.iterator().next().get("_id"), is("hello")); - assertThat(result.iterator().next().getValues(Constants.DEFAULT_FIELD)[0], is("hello")); - assertThat(result.iterator().next().getValues(Constants.DEFAULT_FIELD)[1], is("value")); - } - - @Test - public void testForLoopOverArray() throws Exception { - final DocumentConverter converter = new DocumentConverter( - context, - view("function(doc) {var ret=new Document(); for (var key in doc.arr) {ret.add(doc.arr[key]); } return ret; }")); - final Collection result = converter.convert( - doc("{_id:\"hello\", arr:[0,1,2,3]}"), - settings(), - null); - assertThat(result.size(), is(1)); - assertThat(result.iterator().next().get("_id"), is("hello")); - assertThat(result.iterator().next().getValues(Constants.DEFAULT_FIELD)[0], is("0")); - assertThat(result.iterator().next().getValues(Constants.DEFAULT_FIELD)[1], is("1")); - assertThat(result.iterator().next().getValues(Constants.DEFAULT_FIELD)[2], is("2")); - assertThat(result.iterator().next().getValues(Constants.DEFAULT_FIELD)[3], is("3")); - } - - @Test - public void testForEverything() throws Exception { - final DocumentConverter converter = new DocumentConverter( - context, - view("function(doc) {var ret=new Document(); " - + "function idx(obj) {for (var key in obj) " - + "{switch (typeof obj[key]) {case 'object':idx(obj[key]); break; " - + "case 'function': break; default: ret.add(obj[key]); break;} } }; idx(doc); return ret; }")); - - final Collection result = converter.convert( - doc("{_id:\"hello\", l1: { l2: {l3:[\"v3\", \"v4\"]}}}"), - settings(), - null); - assertThat(result.iterator().next().getValues(Constants.DEFAULT_FIELD), hasItemInArray("hello")); - assertThat(result.iterator().next().getValues(Constants.DEFAULT_FIELD), hasItemInArray("v3")); - assertThat(result.iterator().next().getValues(Constants.DEFAULT_FIELD), hasItemInArray("v4")); - } - - @Test - public void testNullReturn() throws Exception { - final DocumentConverter converter = new DocumentConverter( - context, - view("function(doc) {return null;}")); - final Collection result = converter.convert(doc("{_id:\"hello\"}"), settings(), null); - assertThat(result.size(), is(0)); - } - - @Test - public void testUndefinedReturn() throws Exception { - final DocumentConverter converter = new DocumentConverter( - context, - view("function(doc) {return doc.nope;}")); - final Collection result = converter.convert(doc("{_id:\"hello\"}"), settings(), null); - assertThat(result.size(), is(0)); - } - - @Test - public void testRuntimeException() throws Exception { - LOG.warn("You can ignore the following exception stack trace."); - final DocumentConverter converter = new DocumentConverter( - context, - view("function(doc) {throw {bad : \"stuff\"}}")); - final Collection result = converter.convert(doc("{_id:\"hello\"}"), settings(), null); - LOG.warn("You can ignore the preceding exception stack trace."); - assertThat(result.size(), is(0)); - } - - @Test - public void testJSONStringify() throws Exception { - final DocumentConverter converter = new DocumentConverter( - context, - view("function(doc) {var ret=new Document(); " - + " ret.add(JSON.stringify({\"foo\":\"bar\"}), {\"field\":\"s\",\"store\":\"yes\"}); return ret;}")); - final Collection result = converter.convert( - doc("{_id:\"hello\"}"), settings(), null); - assertThat(result.size(), is(1)); - assertThat(result.iterator().next().getValues("s")[0], is("{\"foo\":\"bar\"}")); - } - - @Test(expected = EvaluatorException.class) - public void testBadCode() throws Exception { - final DocumentConverter converter = new DocumentConverter( - context, - view("function(doc) { if (doc.) return null; }")); - converter.convert(doc("{_id:\"hello\"}"), settings(), null); - } - - @Test - public void testNullAddsAreIgnored() throws Exception { - final DocumentConverter converter = new DocumentConverter( - context, - view("function(doc) {var ret=new Document(); ret.add(doc.nope); return ret;}")); - final Collection result = converter.convert(doc("{_id:\"hello\"}"), settings(), null); - assertThat(result.size(), is(1)); - } - - @Test - public void testQuoteRemoval() throws Exception { - final DocumentConverter converter = new DocumentConverter( - context, - view("\"function(doc) {return new Document();}\"")); - final Collection result = converter.convert(doc("{_id:\"hello\"}"), settings(), null); - assertThat(result.size(), is(1)); - assertThat(result.iterator().next().get("_id"), is("hello")); - } - - @Test - public void testNoReturnValue() throws Exception { - final String fun = "function(doc) { }"; - final DocumentConverter converter = new DocumentConverter(context, view(fun)); - final Collection result = converter.convert(doc("{_id:\"hi\"}"), settings(), null); - assertThat(result.size(), is(0)); - } - - @Test - public void testDefaultValue() throws Exception { - final String fun = "function(doc) { var ret=new Document(); ret.add(doc['arr'].join(' ')); return ret; }"; - final DocumentConverter converter = new DocumentConverter(context, view(fun)); - final Collection result = converter.convert( - doc("{_id:\"hi\", arr:[\"1\",\"2\"]}"), - settings(), - null); - assertThat(result.size(), is(1)); - assertThat(result.iterator().next().get("default"), is("1 2")); - } - - @Test - public void testNullValue() throws Exception { - final String fun = "function(doc) { var ret=new Document(); ret.add(doc.foo); return ret; }"; - final DocumentConverter converter = new DocumentConverter(context, view(fun)); - final Collection result = converter.convert( - doc("{_id:\"hi\", foo:null}"), - settings(), - null); - assertThat(result.size(), is(1)); - assertThat(result.iterator().next().get("foo"), is(nullValue())); - } - - @Test - public void testLongValue() throws Exception { - final String fun = "function(doc) { var ret=new Document(); ret.add(12, {type:\"long\", field:\"num\"}); return ret; }"; - final DocumentConverter converter = new DocumentConverter(context, view(fun)); - final Collection result = converter.convert( - doc("{_id:\"hi\"}"), - settings(), - null); - assertThat(result.size(), is(1)); - assertThat(result.iterator().next().getField("num"), is(LongPoint.class)); - } - - @Test - public void testDateString() throws Exception { - final String fun = "function(doc) { var ret=new Document(); ret.add(\"2009-01-01\", {type:\"date\", field:\"num\"}); return ret; }"; - final DocumentConverter converter = new DocumentConverter(context, view(fun)); - final Collection result = converter.convert( - doc("{_id:\"hi\"}"), - settings(), - null); - assertThat(result.size(), is(1)); - assertThat(result.iterator().next().getField("num"), is(LongPoint.class)); - } - - @Test - public void testDateObject() throws Exception { - final String fun = "function(doc) { var ret=new Document(); ret.add(new Date(2010,8,13), {type:\"date\", field:\"num\"}); return ret; }"; - final DocumentConverter converter = new DocumentConverter(context, view(fun)); - final Collection result = converter.convert( - doc("{_id:\"hi\"}"), - settings(), - null); - assertThat(result.size(), is(1)); - assertThat(result.iterator().next().getField("num"), is(LongPoint.class)); - assertThat((Long) (result.iterator().next().getField("num")).numericValue(), is(1284332400000L)); - } - - @Test - public void testDateObject2() throws Exception { - final String fun = "function(doc) { var ret=new Document(); ret.add(new Date(\"January 6, 1972 16:05:00\"), {type:\"date\", field:\"num\"}); return ret; }"; - final DocumentConverter converter = new DocumentConverter(context, view(fun)); - final Collection result = converter.convert( - doc("{_id:\"hi\"}"), - settings(), - null); - assertThat(result.size(), is(1)); - assertThat(result.iterator().next().getField("num"), is(LongPoint.class)); - assertThat((Long) (result.iterator().next().getField("num")).numericValue(), is(63561900000L)); - } - - @Test - public void testParseInt() throws Exception { - final String fun = "function(doc) { var ret=new Document(); ret.add(parseInt(\"12.5\"), {type:\"int\", field:\"num\"}); return ret; }"; - final DocumentConverter converter = new DocumentConverter(context, view(fun)); - final Collection result = converter.convert( - doc("{_id:\"hi\"}"), - settings(), - null); - assertThat(result.size(), is(1)); - assertThat(result.iterator().next().getField("num"), is(IntPoint.class)); - } - - @Test - public void testConditionalOnNulls() throws Exception { - final String fun = "function(doc) { if (doc.foo && doc.bar) { return new Document(); }; return null; }"; - final DocumentConverter converter = new DocumentConverter(context, - view(fun)); - final Collection result = converter.convert( - doc("{_id:\"hi\", foo: null, bar: null}"), settings(), null); - assertThat(result.size(), is(0)); - } - - private CouchDocument doc(final String json) throws JSONException { - return new CouchDocument(new JSONObject(json)); - } - - private ViewSettings settings() { - return ViewSettings.getDefaultSettings(); - } - - private View view(final String fun) throws JSONException { - final JSONObject json = new JSONObject(); - json.put("index", fun); - return new View(null, json); - } - -} diff --git a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/JsonTest.java b/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/JsonTest.java deleted file mode 100644 index 837a74e31c..0000000000 --- a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/JsonTest.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import org.json.JSONObject; -import org.junit.Test; - -public class JsonTest { - - @Test - public void testEscapedChars() throws Exception { - final String str = "{\"seq\":1503,\"id\":\"11dca825e8b19e40bd675345e05afa24\",\"changes\":[{\"rev\":\"2-bb1fba3e33ed2e8b78412fe27c8c6474\"}],\"doc\":{\"_id\":\"11dca825e8b19e40bd675345e05afa24\",\"_rev\":\"2-bb1fba3e33ed2e8b78412fe27c8c6474\",\"query_params\":{\"{\\\"action\\\":\\\"answer\\\",\\\"session-id\\\":41,\\\"answer\\\":5}\":\"\"},\"stack_trace\":\" File \\\"/usr/local/lib/python2.6/dist-packages/Django-1.2.1-py2.6.egg/django/core/handlers/base.py\\\", line 95, in get_response\\n response = middleware_method(request, callback, callback_args, callback_kwargs)\\n File \\\"/var/src/bhoma/bhoma/middleware.py\\\", line 37, in process_view\\n return login_required(view_func)(request, *view_args, **view_kwargs)\\n File \\\"/usr/local/lib/python2.6/dist-packages/Django-1.2.1-py2.6.egg/django/contrib/auth/decorators.py\\\", line 25, in _wrapped_view\\n return view_func(request, *args, **kwargs)\\n File \\\"/var/src/bhoma/bhoma/apps/xforms/views.py\\\", line 74, in player_proxy\\n response, errors = post_data(data, settings.XFORMS_PLAYER_URL, content_type=\\\"text/json\\\")\\n File \\\"/var/src/bhoma/bhoma/utils/post.py\\\", line 34, in post_data\\n\",\"doc_type\":\"ExceptionRecord\",\"url\":\"http://10.10.10.10/xforms/player_proxy\",\"clinic_id\":\"5010110\",\"date\":\"2010-09-08T14:39:11Z\",\"message\":\"[Errno 24] Too many open files: '/tmp/tmp8xIQb7'\",\"type\":\"\"}}"; - new JSONObject(str); - } - -} diff --git a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/PathPartsTest.java b/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/PathPartsTest.java deleted file mode 100644 index 0c353a06c7..0000000000 --- a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/PathPartsTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import org.junit.Test; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.assertThat; - -public class PathPartsTest { - - @Test - public void testSearchPath() { - final PathParts parts = new PathParts( - "/local/db1/_design/foo/by_subject"); - assertThat(parts.getKey(), is("local")); - assertThat(parts.getDatabaseName(), is("db1")); - assertThat(parts.getDesignDocumentName(), is("_design/foo")); - assertThat(parts.getViewName(), is("by_subject")); - assertThat(parts.getCommand(), is(nullValue())); - } - - @Test - public void testCommandPath() { - final PathParts parts = new PathParts( - "/local/db1/_design/foo/by_subject/_expunge"); - assertThat(parts.getKey(), is("local")); - assertThat(parts.getDatabaseName(), is("db1")); - assertThat(parts.getDesignDocumentName(), is("_design/foo")); - assertThat(parts.getViewName(), is("by_subject")); - assertThat(parts.getCommand(), is("_expunge")); - } - - @Test - public void testCleanupPath() { - final PathParts parts = new PathParts( - "/local/db1/_cleanup"); - assertThat(parts.getKey(), is("local")); - assertThat(parts.getDatabaseName(), is("db1")); - assertThat(parts.getCommand(), is("_cleanup")); - } - -} diff --git a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/TikaTest.java b/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/TikaTest.java deleted file mode 100644 index 587f7f6a23..0000000000 --- a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/TikaTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene; - -import org.apache.lucene.document.Document; -import org.junit.Before; -import org.junit.Test; - -import java.io.IOException; -import java.io.InputStream; - -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThat; - -public class TikaTest { - - private Document doc; - - @Before - public void setup() { - doc = new Document(); - } - - @Test - public void testPDF() throws IOException { - parse("paxos-simple.pdf", "application/pdf", "foo"); - assertThat(doc.getField("foo"), not(nullValue())); - } - - @Test - public void testXML() throws IOException { - parse("example.xml", "text/xml", "bar"); - assertThat(doc.getField("bar"), not(nullValue())); - } - - @Test - public void testWord() throws IOException { - parse("example.doc", "application/msword", "bar"); - assertThat(doc.getField("bar"), not(nullValue())); - assertThat(doc.get("bar"), containsString("The express mission of the organization")); - } - - private void parse(final String resource, final String type, final String field) throws IOException { - final InputStream in = getClass().getClassLoader().getResourceAsStream(resource); - try { - Tika.INSTANCE.parse(in, type, field, doc); - } finally { - in.close(); - } - } - -} diff --git a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/CouchDocumentTest.java b/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/CouchDocumentTest.java deleted file mode 100644 index 308729e42f..0000000000 --- a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/CouchDocumentTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.couchdb; - -import org.json.JSONObject; -import org.junit.Test; - -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; - -public class CouchDocumentTest { - - @Test(expected = IllegalArgumentException.class) - public void notValidDocument() throws Exception { - new CouchDocument(new JSONObject("{}")); - } - - @Test - public void validDocument() throws Exception { - final CouchDocument doc = new CouchDocument(new JSONObject("{_id:\"hello\"}")); - assertThat(doc.getId(), is("hello")); - } - - @Test - public void asJson() throws Exception { - final JSONObject json = new JSONObject("{_id:\"hello\"}"); - final CouchDocument doc = new CouchDocument(json); - assertThat(doc.asJson(), is(json)); - } - -} diff --git a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/DesignDocumentTest.java b/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/DesignDocumentTest.java deleted file mode 100644 index e2cae9793b..0000000000 --- a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/DesignDocumentTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.couchdb; - -import org.json.JSONObject; -import org.junit.Test; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertThat; - -public class DesignDocumentTest { - - @Test(expected = IllegalArgumentException.class) - public void notDesignDocument() throws Exception { - new DesignDocument(new JSONObject("{_id:\"hello\"}")); - } - - @Test - public void noViews() throws Exception { - final DesignDocument ddoc = new DesignDocument(new JSONObject("{_id:\"_design/hello\"}")); - assertThat(ddoc.getAllViews().size(), is(0)); - } - - @Test - public void views() throws Exception { - final JSONObject view = new JSONObject(); - view.put("index", "function(doc) { return null; }"); - - final JSONObject fulltext = new JSONObject(); - fulltext.put("foo", view); - - final JSONObject json = new JSONObject(); - json.put("_id", "_design/hello"); - json.put("fulltext", fulltext); - - final DesignDocument ddoc = new DesignDocument(json); - assertThat(ddoc.getView("foo"), notNullValue()); - assertThat(ddoc.getAllViews().size(), is(1)); - } - -} diff --git a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/UpdateSequenceTest.java b/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/UpdateSequenceTest.java deleted file mode 100644 index 369cf9d06e..0000000000 --- a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/UpdateSequenceTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.couchdb; - -import org.junit.Test; - -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertThat; - -public class UpdateSequenceTest { - - @Test - public void couchdbSequence() { - assertThat(UpdateSequence.parseUpdateSequence("1234"), notNullValue()); - } - - @Test - public void bigcouch3Sequence() { - assertThat( - UpdateSequence - .parseUpdateSequence("79521-g1AAAAGbeJzLYWBg4MhgTmEQT8pMT84vTc5wMDQ30jM00zO0BG" - + "JjgxygAqZEhiT5____ZyUxMKi1EVSdpAAkk-yhGtRdCWtwAGmIh9lwi7CGBJCGepgN0gQ" - + "15LEASYYGIAXUMx-syYlITQsgmvaDneZDpKYDEE33wZpOE6npAUQTJBA6sgABPG9K"), - notNullValue()); - } - - @Test - public void bigcouch4Sequence() { - assertThat( - UpdateSequence - .parseUpdateSequence("[79521,\"g1AAAAGbeJzLYWBg4MhgTmEQT8pMT84vTc5wMDQ30jM00zO0BG" - + "JjgxygAqZEhiT5____ZyUxMKi1EVSdpAAkk-yhGtRdCWtwAGmIh9lwi7CGBJCGepgN0gQ" - + "15LEASYYGIAXUMx-syYlITQsgmvaDneZDpKYDEE33wZpOE6npAUQTJBA6sgABPG9K\"]"), - notNullValue()); - } -} diff --git a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/ViewTest.java b/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/ViewTest.java deleted file mode 100644 index 3dd1458fe8..0000000000 --- a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/couchdb/ViewTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.couchdb; - -import org.json.JSONObject; -import org.junit.Test; - -public class ViewTest { - - @Test(expected = IllegalArgumentException.class) - public void noIndex() throws Exception { - new View(null, new JSONObject("{}")); - } - - @Test - public void index() throws Exception { - final JSONObject json = new JSONObject(); - json.put("index", "function(doc) { return null; }"); - - new View(null, json); - } - -} diff --git a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/util/AnalyzersTest.java b/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/util/AnalyzersTest.java deleted file mode 100644 index cc99d7891a..0000000000 --- a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/util/AnalyzersTest.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.util; - -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.core.KeywordAnalyzer; -import org.apache.lucene.analysis.core.WhitespaceAnalyzer; -import org.apache.lucene.analysis.fr.FrenchAnalyzer; -import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper; -import org.apache.lucene.analysis.standard.StandardAnalyzer; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.json.JSONObject; -import org.junit.Test; - -import java.io.StringReader; -import java.util.ArrayList; -import java.util.List; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; - -public class AnalyzersTest { - - @Test - public void testStandard() throws Exception { - assertThat(Analyzers.getAnalyzer("standard"), is(StandardAnalyzer.class)); - } - - @Test - public void testFrench() throws Exception { - assertThat(Analyzers.getAnalyzer("french"), is(FrenchAnalyzer.class)); - } - - @Test - public void testWhitespace() throws Exception { - assertThat(Analyzers.getAnalyzer("whitespace"), is(WhitespaceAnalyzer.class)); - } - - @Test - public void testPerField() throws Exception { - final Analyzer analyzer = Analyzers.getAnalyzer("perfield:{name:\"standard\",age:\"keyword\"}"); - assertThat(analyzer, is(PerFieldAnalyzerWrapper.class)); - assertThat(analyzer.toString(), containsString("default=org.apache.lucene.analysis.standard.StandardAnalyzer")); - assertThat(analyzer.toString(), containsString("name=org.apache.lucene.analysis.standard.StandardAnalyzer")); - assertThat(analyzer.toString(), containsString("age=org.apache.lucene.analysis.core.KeywordAnalyzer")); - } - - @Test - public void testPerFieldDefault() throws Exception { - final Analyzer analyzer = Analyzers.getAnalyzer("perfield:{default:\"keyword\"}"); - assertThat(analyzer, is(PerFieldAnalyzerWrapper.class)); - assertThat(analyzer.toString(), containsString("default=org.apache.lucene.analysis.core.KeywordAnalyzer")); - } - - @Test - public void testNGramInstance() throws Exception { - final Analyzer analyzer = Analyzers.getAnalyzer("ngram"); - assertThat(analyzer.toString(), containsString("NGramAnalyzer")); - } - - @Test - public void testClassInstance() throws Exception { - final JSONObject obj = new JSONObject("{ \"class\": \"org.apache.lucene.analysis.core.KeywordAnalyzer\" }"); - final Analyzer analyzer = Analyzers.getAnalyzer(obj); - assertThat(analyzer, is(KeywordAnalyzer.class)); - } - - @Test - public void testClassInstance2() throws Exception { - final JSONObject obj = new JSONObject("{ \"class\": \"org.apache.lucene.analysis.nl.DutchAnalyzer\", \"params\": [] }"); - final Analyzer analyzer = Analyzers.getAnalyzer(obj); - assertThat(analyzer, is(org.apache.lucene.analysis.nl.DutchAnalyzer.class)); - } - - @Test - public void testClassInstance3() throws Exception { - final JSONObject obj = - new JSONObject("{ \"class\": \"org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer\", \"params\": [ { \"name\": \"useDefaultStopWords\", \"type\": \"boolean\", \"value\": true } ] }"); - final Analyzer analyzer = Analyzers.getAnalyzer(obj); - assertThat(analyzer, is(org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer.class)); - } - - @Test - public void testClassInstance4() throws Exception { - final JSONObject obj = new JSONObject("{ \"german\": {} }"); - final Analyzer analyzer = Analyzers.getAnalyzer(obj); - assertThat(analyzer, is(org.apache.lucene.analysis.de.GermanAnalyzer.class)); - } - - @Test - public void testClassInstance5() throws Exception { - final JSONObject obj = new JSONObject("{ \"cjk\": \"\" }"); - final Analyzer analyzer = Analyzers.getAnalyzer(obj); - assertThat(analyzer, is(org.apache.lucene.analysis.cjk.CJKAnalyzer.class)); - } - - @Test - public void testClassInstance6() throws Exception { - final JSONObject obj = new JSONObject("{ \"ngram\": { \"analyzer\": \"simple\", \"min\": 2, \"max\": 3 } }"); - final Analyzer analyzer = Analyzers.getAnalyzer(obj); - assertThat(analyzer.toString(), containsString("NGramAnalyzer")); - } - - @Test - public void testClassInstance7() throws Exception { - final Analyzer analyzer = Analyzers.getAnalyzer("perfield:{default:\"keyword\", lang_bo:{\"class\":\"org.apache.lucene.analysis.core.WhitespaceAnalyzer\"}, lang_sa:{\"class\":\"org.apache.lucene.analysis.hi.HindiAnalyzer\"}}"); - assertThat(analyzer, is(PerFieldAnalyzerWrapper.class)); - assertThat(analyzer.toString(), containsString("default=org.apache.lucene.analysis.core.KeywordAnalyzer")); - assertThat(analyzer.toString(), containsString("lang_bo=org.apache.lucene.analysis.core.WhitespaceAnalyzer")); - assertThat(analyzer.toString(), containsString("lang_sa=org.apache.lucene.analysis.hi.HindiAnalyzer")); - } - - @Test - public void testClassInstance8() throws Exception { - final Analyzer analyzer = Analyzers.fromSpec("{\"perfield\":{\"default\": \"keyword\",\"lang_bo\": {\"class\": \"org.apache.lucene.analysis.core.WhitespaceAnalyzer\"},\"lang_sa\": {\"class\": \"org.apache.lucene.analysis.hi.HindiAnalyzer\"}}}"); - assertThat(analyzer, is(PerFieldAnalyzerWrapper.class)); - assertThat(analyzer.toString(), containsString("default=org.apache.lucene.analysis.core.KeywordAnalyzer")); - assertThat(analyzer.toString(), containsString("lang_bo=org.apache.lucene.analysis.core.WhitespaceAnalyzer")); - assertThat(analyzer.toString(), containsString("lang_sa=org.apache.lucene.analysis.hi.HindiAnalyzer")); - } - - @Test - public void testNGramTokens() throws Exception { - assertThat(analyze("ngram:{\"analyzer\":\"simple\"}", "hey there"), is(new String[]{"h", "he", "e", "ey", "y", "t", "th", "h", "he", "e", "er", "r", "re", "e"})); - } - - @Test - public void testNGramMinMax() throws Exception { - assertThat(analyze("ngram:{\"analyzer\":\"simple\",\"min\":2,\"max\":3}", "hello there"), is(new String[]{"he", "hel", "el", "ell", "ll", "llo", "lo", "th", "the", "he", "her", "er", "ere", "re"})); - } - - @Test - public void testEmailAddresses() throws Exception { - assertThat(analyze("standard", "foo@bar.com"), is(new String[]{"foo", "bar.com"})); - assertThat(analyze("classic", "foo@bar.com"), is(new String[]{"foo@bar.com"})); - } - - private String[] analyze(final String analyzerName, final String text) throws Exception { - final Analyzer analyzer = Analyzers.getAnalyzer(analyzerName); - final TokenStream stream = analyzer.tokenStream("default", new StringReader(text)); - stream.reset(); - final List result = new ArrayList(); - while (stream.incrementToken()) { - final CharTermAttribute c = stream.getAttribute(CharTermAttribute.class); - result.add(c.toString()); - } - return result.toArray(new String[0]); - } -} diff --git a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/util/UtilsTest.java b/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/util/UtilsTest.java deleted file mode 100644 index 2f9019091f..0000000000 --- a/third-party/couchdb-lucene/src/test/java/com/github/rnewson/couchdb/lucene/util/UtilsTest.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Robert Newson - * - * Licensed 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 com.github.rnewson.couchdb.lucene.util; - -import org.junit.Test; - -import static org.junit.Assert.assertArrayEquals; - - -public class UtilsTest { - - @Test - public void testSplitOnCommas() { - assertArrayEquals(new String[]{"foo", "bar"}, Utils.splitOnCommas("foo,bar")); - } - - @Test - public void testSplitOnCommasWithEmbeddedCommas() { - assertArrayEquals(new String[]{"\"fo,o\"", "bar"}, Utils.splitOnCommas("\"fo,o\",bar")); - } - -} diff --git a/third-party/couchdb-lucene/src/test/resources/couchdb-lucene.ini b/third-party/couchdb-lucene/src/test/resources/couchdb-lucene.ini deleted file mode 100644 index acbe508ace..0000000000 --- a/third-party/couchdb-lucene/src/test/resources/couchdb-lucene.ini +++ /dev/null @@ -1,8 +0,0 @@ -[lucene] -dir=target/indexes -host=localhost -port=5985 -timeout=5000 - -[local] -url=http://localhost:5984/ diff --git a/third-party/couchdb-lucene/src/test/resources/example.doc b/third-party/couchdb-lucene/src/test/resources/example.doc deleted file mode 100644 index ecfe186852..0000000000 --- a/third-party/couchdb-lucene/src/test/resources/example.doc +++ /dev/null @@ -1,1389 +0,0 @@ -{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff0\deff0\stshfdbch0\stshfloch0\stshfhich0\stshfbi0\deflang1033\deflangfe1033{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f1\fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;} -{\f2\fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New;}{\f137\froman\fcharset238\fprq2 Times New Roman CE;}{\f138\froman\fcharset204\fprq2 Times New Roman Cyr;}{\f140\froman\fcharset161\fprq2 Times New Roman Greek;} -{\f141\froman\fcharset162\fprq2 Times New Roman Tur;}{\f142\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f143\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f144\froman\fcharset186\fprq2 Times New Roman Baltic;} -{\f145\froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f147\fswiss\fcharset238\fprq2 Arial CE;}{\f148\fswiss\fcharset204\fprq2 Arial Cyr;}{\f150\fswiss\fcharset161\fprq2 Arial Greek;}{\f151\fswiss\fcharset162\fprq2 Arial Tur;} -{\f152\fbidi \fswiss\fcharset177\fprq2 Arial (Hebrew);}{\f153\fbidi \fswiss\fcharset178\fprq2 Arial (Arabic);}{\f154\fswiss\fcharset186\fprq2 Arial Baltic;}{\f155\fswiss\fcharset163\fprq2 Arial (Vietnamese);} -{\f157\fmodern\fcharset238\fprq1 Courier New CE;}{\f158\fmodern\fcharset204\fprq1 Courier New Cyr;}{\f160\fmodern\fcharset161\fprq1 Courier New Greek;}{\f161\fmodern\fcharset162\fprq1 Courier New Tur;} -{\f162\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);}{\f163\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);}{\f164\fmodern\fcharset186\fprq1 Courier New Baltic;}{\f165\fmodern\fcharset163\fprq1 Courier New (Vietnamese);}} -{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0; -\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\red255\green153\blue0;\red255\green255\blue255;}{\stylesheet{ -\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \snext0 Normal;}{\*\cs10 \additive \ssemihidden -Default Paragraph Font;}{\*\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tblind0\tblindtype3\tscellwidthfts0\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv -\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \fs20\lang1024\langfe1024\cgrid\langnp1024\langfenp1024 \snext11 \ssemihidden Normal Table;}} -{\*\latentstyles\lsdstimax156\lsdlockeddef0}{\*\pgptbl {\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}}{\*\rsidtbl \rsid4394652\rsid5992012\rsid12784854\rsid13466995\rsid14303898}{\*\generator Microsoft Word 11.0.0000;}{\info{\operator vbhambur} -{\creatim\yr2009\mo2\dy19\hr20\min6}{\revtim\yr2009\mo2\dy19\hr20\min8}{\version3}{\edmins1}{\nofpages1}{\nofwords151}{\nofchars863}{\nofcharsws1012}{\vern24611}{\*\password 00000000}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wor -dml}}\paperw12240\paperh15840\margl1800\margr1800\margt1440\margb1440\gutter0\ltrsect -\widowctrl\ftnbj\aenddoc\donotembedsysfont0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120\dghorigin1701\dgvorigin1984\dghshow0\dgvshow3 -\jcompress\viewkind4\viewscale100\rsidroot5992012 \fet0{\*\wgrffmtfilter 013f}\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2 -\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6 -\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang -{\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\sb100\sa100\keepn\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \ab\af0\afs36 -\ltrch\fcs0 \b\fs36\insrsid13466995 Philanthropy -\par }\pard \ltrpar\ql \li0\ri0\sb100\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 In 2004, Google formed a non-profit philanthropic wing, }{\field{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 -\insrsid13466995 HYPERLINK "/wiki/Google.org"}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid12784854 {\*\datafield -00d0c9ea79f9bace118c8200aa004ba90b0200000001000000e0c9ea79f9bace118c8200aa004ba90b220000002f00770069006b0069002f0047006f006f0067006c0065002e006f0072006700000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf2\insrsid13466995 Google.org}}}\sectd -\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 , giving it a starting fund of $1 billion.}{\field{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 HYPERLINK "\\\\l "cite_note-philanthropy-41""}{\rtlch\fcs1 \af0 -\ltrch\fcs0 \insrsid12784854 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b02000000010000000303000000000000c0000000000000460000040000005c6c2000ffffadde00000000000000000000000000000000000000000000000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0 -\ul\cf2\insrsid13466995 [42]}}}\sectd \linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 The express mission of the organization is to help with the issues of }{\field{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 -HYPERLINK "/wiki/Climate_change"}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid12784854 {\*\datafield -00d0c9ea79f9bace118c8200aa004ba90b0200000001000000e0c9ea79f9bace118c8200aa004ba90b2a0000002f00770069006b0069002f0043006c0069006d006100740065005f006300680061006e0067006500000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf2\insrsid13466995 -climate change}}}\sectd \linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 (see also }{\field{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 HYPERLINK "/wiki/Global_warming"}{\rtlch\fcs1 \af0 \ltrch\fcs0 -\insrsid12784854 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000001000000e0c9ea79f9bace118c8200aa004ba90b2a0000002f00770069006b0069002f0047006c006f00620061006c005f007700610072006d0069006e006700000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0 -\ul\cf2\insrsid13466995 global warming}}}\sectd \linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 ), global public health, and }{\field{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 HYPERLINK "/wiki/Global_poverty"}{ -\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid12784854 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000001000000e0c9ea79f9bace118c8200aa004ba90b2a0000002f00770069006b0069002f0047006c006f00620061006c005f0070006f0076006500720074007900000000}}}{\fldrslt { -\rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf2\insrsid13466995 global poverty}}}\sectd \linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 . Among its first projects is to develop a viable }{\field{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 -\insrsid13466995 HYPERLINK "/wiki/Plug-in_hybrid"}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid12784854 {\*\datafield -00d0c9ea79f9bace118c8200aa004ba90b0200000001000000e0c9ea79f9bace118c8200aa004ba90b2a0000002f00770069006b0069002f0050006c00750067002d0069006e005f00680079006200720069006400000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf2\insrsid13466995 -plug-in hybrid}}}\sectd \linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 }{\field{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 HYPERLINK "/wiki/Electric_vehicle"}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid12784854 -{\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000001000000e0c9ea79f9bace118c8200aa004ba90b2e0000002f00770069006b0069002f0045006c006500630074007200690063005f00760065006800690063006c006500000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0 -\ul\cf2\insrsid13466995 electric vehicle}}}\sectd \linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 that can attain 100 }{\field{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 -HYPERLINK "/wiki/Fuel_economy_in_automobiles"}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid12784854 {\*\datafield -00d0c9ea79f9bace118c8200aa004ba90b0200000001000000e0c9ea79f9bace118c8200aa004ba90b440000002f00770069006b0069002f004600750065006c005f00650063006f006e006f006d0079005f0069006e005f006100750074006f006d006f00620069006c0065007300000000}}}{\fldrslt {\rtlch\fcs1 -\af0 \ltrch\fcs0 \ul\cf2\insrsid13466995 mpg}}}\sectd \linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 . The current director is Dr. }{\field{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 -HYPERLINK "/wiki/Larry_Brilliant"}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid12784854 {\*\datafield -00d0c9ea79f9bace118c8200aa004ba90b0200000001000000e0c9ea79f9bace118c8200aa004ba90b2c0000002f00770069006b0069002f004c0061007200720079005f004200720069006c006c00690061006e007400000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf2\insrsid13466995 -Larry Brilliant}}}\sectd \linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 .}{\field{\*\fldinst {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 HYPERLINK "\\\\l "cite_note-42""}{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid12784854 -{\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b02000000010000000303000000000000c0000000000000460000040000005c6c2000ffffadde00000000000000000000000000000000000000000000000000}}}{\fldrslt {\rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf2\insrsid13466995 [43]}}}\sectd -\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 -\par }\pard \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid13466995 -\par -\par \ltrrow}\trowd \irow0\irowband0\ltrrow\ts11\trgaph108\trleft-108\trbrdrt\brdrs\brdrw10 \trbrdrl\brdrs\brdrw10 \trbrdrb\brdrs\brdrw10 \trbrdrr\brdrs\brdrw10 \trftsWidth1\trpaddl108\trpaddr108\trpaddfl3\trpaddfr3\tblind0\tblindtype3 \clvertalt\clbrdrt -\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx1663\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 -\cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx3434\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx5205\clvertalt\clbrdrt\brdrs\brdrw10 -\clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx6976\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 -\cltxlrtb\clftsWidth3\clwWidth1772\clshdrawnil \cellx8748\pard \ltrpar\ql \li0\ri0\nowidctlpar\intbl\wrapdefault\faauto\rin0\lin0 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 sfg\cell 768\cell 89\cell 878\cell 76y\cell }\pard \ltrpar -\ql \li0\ri0\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 \trowd \irow0\irowband0\ltrrow\ts11\trgaph108\trleft-108\trbrdrt\brdrs\brdrw10 \trbrdrl\brdrs\brdrw10 \trbrdrb -\brdrs\brdrw10 \trbrdrr\brdrs\brdrw10 \trftsWidth1\trpaddl108\trpaddr108\trpaddfl3\trpaddfr3\tblind0\tblindtype3 \clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 -\cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx1663\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx3434\clvertalt\clbrdrt\brdrs\brdrw10 -\clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx5205\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 -\cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx6976\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1772\clshdrawnil \cellx8748\row \ltrrow}\pard \ltrpar -\ql \li0\ri0\nowidctlpar\intbl\wrapdefault\faauto\rin0\lin0 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 4cbn\cell 456\cell 576\cell dsf45\cell d56\cell }\pard \ltrpar\ql \li0\ri0\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0 -{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 \trowd \irow1\irowband1\ltrrow\ts11\trgaph108\trleft-108\trbrdrt\brdrs\brdrw10 \trbrdrl\brdrs\brdrw10 \trbrdrb\brdrs\brdrw10 \trbrdrr\brdrs\brdrw10 -\trftsWidth1\trpaddl108\trpaddr108\trpaddfl3\trpaddfr3\tblind0\tblindtype3 \clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx1663\clvertalt\clbrdrt -\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx3434\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 -\cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx5205\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx6976\clvertalt\clbrdrt\brdrs\brdrw10 -\clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1772\clshdrawnil \cellx8748\row \ltrrow}\pard \ltrpar\ql \li0\ri0\nowidctlpar\intbl\wrapdefault\faauto\rin0\lin0 {\rtlch\fcs1 \af0 \ltrch\fcs0 -\insrsid13466995 dsgh\cell 8978\cell 456863457\cell 4sdt45\cell dfy56\cell }\pard \ltrpar\ql \li0\ri0\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 \trowd \irow2\irowband2\ltrrow -\ts11\trgaph108\trleft-108\trbrdrt\brdrs\brdrw10 \trbrdrl\brdrs\brdrw10 \trbrdrb\brdrs\brdrw10 \trbrdrr\brdrs\brdrw10 \trftsWidth1\trpaddl108\trpaddr108\trpaddfl3\trpaddfr3\tblind0\tblindtype3 \clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 -\clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx1663\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil -\cellx3434\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx5205\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 -\clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx6976\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1772\clshdrawnil \cellx8748\row \ltrrow -}\pard \ltrpar\ql \li0\ri0\nowidctlpar\intbl\wrapdefault\faauto\rin0\lin0 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 wertwe\cell 809\cell 568\cell sdf54\cell dfy\cell }\pard \ltrpar -\ql \li0\ri0\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 \trowd \irow3\irowband3\ltrrow\ts11\trgaph108\trleft-108\trbrdrt\brdrs\brdrw10 \trbrdrl\brdrs\brdrw10 \trbrdrb -\brdrs\brdrw10 \trbrdrr\brdrs\brdrw10 \trftsWidth1\trpaddl108\trpaddr108\trpaddfl3\trpaddfr3\tblind0\tblindtype3 \clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 -\cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx1663\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx3434\clvertalt\clbrdrt\brdrs\brdrw10 -\clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx5205\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 -\cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx6976\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1772\clshdrawnil \cellx8748\row \ltrrow}\pard \ltrpar -\ql \li0\ri0\nowidctlpar\intbl\wrapdefault\faauto\rin0\lin0 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 rtjutrh\cell 564\cell 346\cell 56dfg\cell 5645\cell }\pard \ltrpar -\ql \li0\ri0\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid13466995 \trowd \irow4\irowband4\lastrow \ltrrow\ts11\trgaph108\trleft-108\trbrdrt\brdrs\brdrw10 \trbrdrl\brdrs\brdrw10 \trbrdrb -\brdrs\brdrw10 \trbrdrr\brdrs\brdrw10 \trftsWidth1\trpaddl108\trpaddr108\trpaddfl3\trpaddfr3\tblind0\tblindtype3 \clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 -\cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx1663\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx3434\clvertalt\clbrdrt\brdrs\brdrw10 -\clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx5205\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 -\cltxlrtb\clftsWidth3\clwWidth1771\clshdrawnil \cellx6976\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clftsWidth3\clwWidth1772\clshdrawnil \cellx8748\row }\pard \ltrpar -\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid13466995 5678 -\par -\par }{\pard\plain \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af1\afs20\alang1025 \ltrch\fcs0 \f1\fs20\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\object\objemb\objw2400\objh2400{\*\objclass PBrush} -{\*\objdata 010500000200000007000000504272757368000000000000000000402c0100 -424d362c0100000000003600000028000000a0000000a00000000100180000000000002c010000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff -0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff0000ff0000ff0000ff -0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff -0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff -0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff0000ff0000ff0000ff0000ff0000ff -0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff -0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff -0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff -0000ff0000ff0000ff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000 -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000 -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000 -ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000 -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff -ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000 -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffff -ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000 -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff -ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff -ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff000000ffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -000000ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff -ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff -ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000ffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000ffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff -ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -000000ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000 -0000000001050000050000000d0000004d45544146494c455049435400610900009ff6ffff006d00000800610961090000 -0100090000037c36000000002719000000001610000026060f002220574d46430100000000000100cbf3000000000200000000200000e8190000e8390000010000006c00000000000000000000005a0000005a00000000000000000000001b0b00001b0b000020454d4600000100e83900000c0000000100000000000000 -0000000000000000000400000003000040010000f000000000000000000000000000000000e2040080a90300460000002c00000020000000454d462b014001001c000000100000000210c0db0100000060000000600000004600000000060000f4050000454d462b224004000c000000000000001e4009000c0000000000 -0000244001000c000000000000003040020010000000040000000000803f214007000c00000000000000084000054c050000400500000210c0db01000000000000000000000000000000000000000100000089504e470d0a1a0a0000000d49484452000000a0000000a0080200000004adf07a000000017352474200aece -1ce9000000097048597300001a0b00001a0b01b149da8a000004c849444154785eed9cdb92e2300c4497fdff7f66bd45d514032438b6a596e4c3c33c4cd96aa98f940b97dceef7fb1f5e751df85bb7342afbef00808bf70180015cdc81e2e531c1002eee40f1f29860001777a078794c30808b3b50bc3c2618c0c51d285e -1e130ce0e20e142f8f09067071078a97c70403b8b803c5cb6382015cdc81e2e531c1002eee40f1f29860001777a078794c30808b3b50bc3c2618c0c51d285e1e130ce0e20e142f8f09067071078a97c70403b8b803c5cb6382015cdc8195e5dd6eb795e156c40a37c1cda3c76b4575ae3162e67c933f65e7c517793ec34d -d10a69c93ffe0e0759be51904d19a22f30f6055c95e847c0ed9fa186d8648283107536fa47ce59f7fca8be067010a24723b5fcc4f631e033d7388c0701c7240ae0f7ceeb02fc7e0310ea42f168409dc7e845ce59fdc884aefbe086f3e5e573d04365de812ec0933231df01982ceaebf6c73df1d765d60b3c005bd740fc13 -073c00ab7a59a5fb63b73c8196890760ed8445384e0a1da80f58686e93960f717dc0728bb51de60458ebb2567d0bc0da22b5eadaf6729a60adc511ce852a077601ac652c1c623fc0c22255d31341d70f70846a376cb2bd000b9b4cd55bae8055453e738d9083679fb902f62cec446b2bc6c50187622949a638e0a3399678 -2d398079038ee36c9c4c4cc17b03362d267e70ffaeda1ab0bfddfe2db83560edfb973eb00580a3cd8d733ece7202c03e9d7b49c5d9f44bb94d2e06f0a48123db3dfb4903d8b3c24eadce65233ca57b3480a5251f8a97640ce05fbcdd18bb0901f875a0ddacf7398cc90047f631726e57db4206f86aa233eb07800d6c99c9 -d06eef1680c7ecabc1787bc0017ee139d67f9dbb9480438cc8e933adfa330cf27bfe77ea4ac09d3da85dd6cf589be791fa2e806738cdec9553df05f0a4d179198b0127322e51aacfdd2c063c3958cedb333206f0b52649c77823c0e9d85c6bbd83d57ac0e97ccf95b01ef0923e750e92883180077be39971d8b7b15a6d00 -1e04dcb6a598e3aea7cd9e7890e831638d472b64f9b43d1c7804bff45a9ec947f559c0974a3a6f94018f06d42d6c1d8e39bcb1bf700ed1fd5ea55c09e005d8229f8ca3008eec514f0bf89c5f7a3279591305b0dbc55af64ebaca781af0a2afbc34dfc30ec1554f43ad9f067cfdf62054fde59399069cd0a1ad0e153b024e -d893e3290378dcbb143b019c02d37892001ef72ec54e00a7c0349e2480c7bd4bb113c029308d2719e5e3c2f10ad879ea00135cbc41000ce0e20e142f8f092e0e78dd45d6a2cf0d8bfbed5ede3ac0eea923d8e30087e81e9712af017062783da903b8c7a5c46b009c185e4fea8b2eb24c2fa1f9da570fc983358b004f64f0 -7dab69f77c975fbac2bd5933005eeab038987bb302584cdc5a9e8b2c6b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f -606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1ff013c087d0a3c1e809b00 -00000049454e44ae426082000840010824000000180000000210c0db01000000030000000000000000000000000000001b40000040000000340000000100000002000000000000bf000000bf0000204300002043030000000000008000000080ffffb5420000008000000080ffffb5422100000008000000620000000c00 -000001000000150000000c00000004000000150000000c00000004000000510000008432000000000000000000005a0000005a00000000000000000000000000000000000000a0000000a000000050000000340000008400000000320000000000002000cc005b0000005b00000028000000a0000000a000000001000400 -00000000000000000000000000000000030000000000000000000000ffffff000000ff00111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111222222222222222222222222222222222222221111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222222211111111111111111111111111111111111111111111111111 -111111111112222222222222222222222222222222111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111222222222222222222222222222222222222 -222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222211111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110111111 -111111111111111111111111111111111111111011111111111111111111111111111111111111111111111111111211111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111 -111111111111111111111111100111111111111111111111111111111111101111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111101111111111111111111111111111111 -111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111110111111111111111111111111111101111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111 -101111111111111111111111111111111111011111111111111111111111111101111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111101111111111111111111111111110111111111111111111011111111 -111111111111111111111111111111111111111111111000000000000000000000000000000000000000000000000000000000110111111111111111111111111110011111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111 -111111111110111111111111111111111111110101111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111011111111111111111111111110110111111111111111110111111111111111111111111111111111 -111111111111111111111111111111111111111111110111111111111111111111111111111111101111111111111111111111110111101111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111110111111111111 -111111111110111110111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111011111111111111111111110111111011111111111111110111111111111111111111111111111111111111111111111111111111 -111111111111111111101111111111111111111111111111111111011111111111111111111111011111101111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111101111111111111111111111011111110111111 -111111111101111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111110111111111111111111111011111111011111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111011111 -111111111111111111111111111111011111111111111111111011111111101111111111111111011111111111111111111111111111111111111111111111110a0d000026060f000a1a574d4643010000000000010000000000000002000000e819000000000000e8390000111111111111111111111111110111111111 -111111111111111111111111110111111111111111111101111111111011111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111110111111111111111111101111111111101111111111111111011111111111111111 -111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111011111111111111111101111111111110111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111 -101111111211111111101111111111111101111111111111110111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111110111111111111111110111111111111110111111111111111011111111111111111111111111111111111111111 -111111111111111111111111111111111011111111111111111111111111111111111011111111111111110111111111111111011111111111111101111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111011111111111111110111111 -111111111101111111111111110111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111101111111111111110111111111111111110111111111111111011111111111111111111111111111111111111111111111111111111111111111 -111111110111111111111111111111111111111111110111111111111110111111111111111111011111111111111101111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111011111111111110111111111111111111101111111111111 -101111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111101111111111110111111111111111111110111111111111110111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111 -111111111111111111101111111111110111111111111111111111011111111111111011111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111110111111111111011111111111111111111101111111111111011111111111111111111111 -111111111111111111111111111111111111111111111111111011111111111111111111111111111111111011111111111011111111111111111111110111111111111101111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111101111 -111111011111111111111111111111011111111111101111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111110111111111011111111111111111111111101111111111110111111111111111111111111111111111111111111111111 -111111111111111111111111110111111111111111111111111111111111111011111111011111111111111111111111110111111111111011111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111101111111101111111111111111111 -111111011111111111011111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111110111111101111111111111111111111111101111111111101111111111111111111111111111111111111111111111111111111111111111111111111 -101111111111111111111111111111111111111011111101111111111111111111111111110111111111110111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111011111110111111111111111111111111111011111111110111111 -111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111101111110111111111111111111111111111101111111111011111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111 -111111111111110111110111111111111111111111111111110111111111101111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111011111011111111111111111111111111111011111111101111111111111111111111111111111 -111111111111111111111111111111111111111111110111111111111111111111111111111111111101111011111111111111111111111111111101111111110111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111110111101111 -111111111111111111111111110111111111011111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111011101111111111111111111111111111111011111111011111111111111111111111111111111111111111111111111111111 -111111111111111111101111111111111111111111111111111111111101101111111111111111111111111111111101111111101111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111110110111111111111111111111111111111 -110111111101111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111010111111111111111111111111111111111011111110111111111111111111111111111111111111111111111111111111111111111111111111111011111 -111111111111111111111111111111111100111111111111111111111111111111111101111111011111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111110011111111111111111111111111111111110111111011111111111111 -111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111011111111111111111111111111111111111011111101111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111 -111111111011111111111111111111111111111111111101111101111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111101111111111111111111111111111111111110111110111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111011110111111111111111111111111111111111111111111111111111111110000001111111111111111111111111111111111111111111111111111111011111111111111 -111111111111111111111101111011111111111111111111111111111111111111111111111111111111111111000001111111111111111111111111111111111111111111111111101111111111111111111111111111111111110111011111111111111111111111111111111111111111111111111111111111111111 -111000001111111111111111111111111111111111111111111110111111111111111111111111111111111111011101111111111111111111111111111111111111111111111111111111111111111111111111000001111111111111111111111111111111111111111011111111111111111111111111111111111101 -101111111111111111111111111111111111111111111111111111111111111111111111111111111000001111111111111111111111111111111111111111111111111111111111111111111111110110111111111111111111111111111111111111111111111111111111111111111111111111111111111111000001 -111111111111111111111111111111111111111111111111111111111111111111010111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111000001111111111111111111111111111111111111111111111111111111111111101011111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111000111111111111111111111111111111111111111111111111111111111110011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111114c0000006400000000000000000000005a0000005a00000000000000000000005b0000005b0000002900aa0000000000000000000000803f00000000000000000000803f000000000000000000000000000000000000000000000000000000000000 -0000220000000c000000ffffffff460000001c00000010000000454d462b024000000c000000000000000e000000140000000000000010000000140000000400000003010800050000000b0200000000050000000c025b005b00030000001e000400000007010400040000000701040027190000410b2000cc00a000a000 -000000005b005b000000000028000000a0000000a00000000100040000000000000000000000000000000000030000000000000000000000ffffff000000ff001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222211111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222 -222222111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222221111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111112222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111112111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111111 -111011111111111111111111111111111111111111111111111111111111111111111111111111111001111111111111111111111111111111111011111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111110111111111 -111111111111111111111111011111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111101111111111111111111111111111011111111111111111011111111111111111111 -111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111110111111111111111111111111111011111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111 -011111111111111111111111111101111111111111111110111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000001101111111111111111111111111100111111111111111110111111111111111111111111111111111111111111111 -111111111111111111111111111111110111111111111111111111111111111111101111111111111111111111111101011111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111110111111111111111111111111 -101101111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111011111111111111111111111101111011111111111111110111111111111111111111111111111111111111111111111111111111111111111111 -111111110111111111111111111111111111111111101111111111111111111111101111101111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111110111111111111111111111101111110111111111111111101 -111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111110111111111111111111111110111111011111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111 -111111111111111111011111111111111111111110111111101111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111101111111111111111111110111111110111111111111111101111111111111111111111111 -111111111111111111111111111111111111111111111111110111111111111111111111111111111111110111111111111111111110111111111011111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111011111 -111111111111110111111111101111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111011111111111111111110111111111110111111111111111101111111111111111111111111111111111111111111111111 -111111111111111111111111110111111111111111111111111111111111101111111111111111110111111111111011111111111111110111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111110111111121111111110111111111111 -110111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111011111111111111111011111111111111011111111111111101111111111111111111111111111111111111111111111111111111111111111111111111 -101111111111111111111111111111111111101111111111111111011111111111111101111111111111110111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111101111111111111111011111111111111110111111111111111011111 -111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111110111111111111111011111111111111111011111111111111101111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111 -111111111111011111111111111011111111111111111101111111111111110111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111101111111111111011111111111111111110111111111111110111111111111111111111111111111 -111111111111111111111111111111111111111111110111111111111111111111111111111111110111111111111011111111111111111111011111111111111011111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111110111111111111 -011111111111111111111101111111111111101111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111011111111111101111111111111111111110111111111111101111111111111111111111111111111111111111111111111111111 -111111111111111111101111111111111111111111111111111111101111111111101111111111111111111111011111111111110111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111110111111111101111111111111111111111101 -111111111110111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111011111111101111111111111111111111110111111111111011111111111111111111111111111111111111111111111111111111111111111111111111011111 -111111111111111111111111111111101111111101111111111111111111111111011111111111101111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111110111111110111111111111111111111111101111111111101111111111111 -111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111011111110111111111111111111111111110111111111110111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111 -111111101111110111111111111111111111111111011111111111011111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111101111111011111111111111111111111111101111111111011111111111111111111111111111111111111 -111111111111111111111111111111111111101111111111111111111111111111111111110111111011111111111111111111111111110111111111101111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111011111011111111111 -111111111111111111011111111110111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111101111101111111111111111111111111111101111111110111111111111111111111111111111111111111111111111111111111111111 -111111111111011111111111111111111111111111111111110111101111111111111111111111111111110111111111011111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111011110111111111111111111111111111111011111 -111101111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111101110111111111111111111111111111111101111111101111111111111111111111111111111111111111111111111111111111111111111111111110111111111111 -111111111111111111111111110110111111111111111111111111111111110111111110111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111011011111111111111111111111111111111011111110111111111111111111111 -111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111101011111111111111111111111111111111101111111011111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111 -110011111111111111111111111111111111110111111101111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111001111111111111111111111111111111111011111101111111111111111111111111111111111111111111111 -111111111111111111111111111111011111111111111111111111111111111111111101111111111111111111111111111111111101111110111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111101111111111111111111111 -111111111111110111110111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111110111111111111111111111111111111111111011111011111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111011111111111111111111111111111111111101111011111111111111111111111111111111111111111111111111111111000000111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111110111101111 -111111111111111111111111111111111111111111111111111111111100000111111111111111111111111111111111111111111111111110111111111111111111111111111111111111011101111111111111111111111111111111111111111111111111111111111111111111100000111111111111111111111111 -111111111111111111111011111111111111111111111111111111111101110111111111111111111111111111111111111111111111111111111111111111111111111100000111111111111111111111111111111111111111101111111111111111111111111111111111110110111111111111111111111111111111 -111111111111111111111111111111111111111111111111100000111111111111111111111111111111111111111111111111111111111111111111111111011011111111111111111111111111111111111111111111111111111111111111111111111111111111111100000111111111111111111111111111111111 -111111111111111111111111111111111101011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000111111111111111111111111111111111111111111111111111111111111110101111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111100011111111111111111111111111111111111111111111111111111111111001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -1111111111111111111111110c00000040092900aa000000000000005b005b0000000000040000002701ffff030000000000}{\result {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid12784854 {\*\shppict -{\pict{\*\picprop\shplid1025{\sp{\sn shapeType}{\sv 75}}{\sp{\sn fFlipH}{\sv 0}}{\sp{\sn fFlipV}{\sv 0}}{\sp{\sn fLine}{\sv 0}}{\sp{\sn fLayoutInCell}{\sv 1}}{\sp{\sn fLayoutInCell}{\sv 1}}} -\picscalex100\picscaley100\piccropl0\piccropr0\piccropt0\piccropb0\picw4233\pich4233\picwgoal2400\pichgoal2400\pngblip\bliptag-78695031{\*\blipuid fb4f3589a5829166a927b41fda22160b} -89504e470d0a1a0a0000000d49484452000000a0000000a0080200000004adf07a000000017352474200aece1ce9000000097048597300001a0b00001a0b01b1 -49da8a000004c849444154785eed9cdb92e2300c4497fdff7f66bd45d514032438b6a596e4c3c33c4cd96aa98f940b97dceef7fb1f5e751df85bb7342afbef00 -808bf70180015cdc81e2e531c1002eee40f1f29860001777a078794c30808b3b50bc3c2618c0c51d285e1e130ce0e20e142f8f09067071078a97c70403b8b803 -c5cb6382015cdc81e2e531c1002eee40f1f29860001777a078794c30808b3b50bc3c2618c0c51d285e1e130ce0e20e142f8f09067071078a97c70403b8b803c5 -cb6382015cdc8195e5dd6eb795e156c40a37c1cda3c76b4575ae3162e67c933f65e7c517793ec34dd10a69c93ffe0e0759be51904d19a22f30f6055c95e847c0 -ed9fa186d8648283107536fa47ce59f7fca8be067010a24723b5fcc4f631e033d7388c0701c7240ae0f7ceeb02fc7e0310ea42f168409dc7e845ce59fdc884ae -fbe086f3e5e573d04365de812ec0933231df01982ceaebf6c73df1d765d60b3c005bd740fc13073c00ab7a59a5fb63b73c8196890760ed8445384e0a1da80f58 -686e93960f717dc0728bb51de60458ebb2567d0bc0da22b5eadaf6729a60adc511ce852a077601ac652c1c623fc0c22255d31341d70f70846a376cb2bd000b9b -4cd55bae8055453e738d9083679fb902f62cec446b2bc6c50187622949a638e0a33996782d398079038ee36c9c4c4cc17b03362d267e70ffaeda1ab0bfddfe2d -b83560edfb973eb00580a3cd8d733ece7202c03e9d7b49c5d9f44bb94d2e06f0a48123db3dfb4903d8b3c24eadce65233ca57b3480a5251f8a97640ce05fbcdd -18bb0901f875a0ddacf7398cc90047f631726e57db4206f86aa233eb07800d6c99c9d06eef1680c7ecabc1787bc0017ee139d67f9dbb9480438cc8e933adfa33 -0cf27bfe77ea4ac09d3da85dd6cf589be791fa2e806738cdec9553df05f0a4d179198b0127322e51aacfdd2c063c3958cedb333206f0b52649c77823c0e9d85c -6bbd83d57ac0e97ccf95b01ef0923e750e92883180077be39971d8b7b15a6d001e04dcb6a598e3aea7cd9e7890e831638d472b64f9b43d1c7804bff45a9ec947 -f559c0974a3a6f94018f06d42d6c1d8e39bcb1bf700ed1fd5ea55c09e005d8229f8ca3008eec514f0bf89c5f7a3279591305b0dbc55af64ebaca781af0a2afbc -34dfc30ec1554f43ad9f067cfdf62054fde59399069cd0a1ad0e153b024ed893e3290378dcbb143b019c02d37892001ef72ec54e00a7c0349e2480c7bd4bb113 -c029308d2719e5e3c2f10ad879ea00135cbc41000ce0e20e142f8f092e0e78dd45d6a2cf0d8bfbed5ede3ac0eea923d8e30087e81e9712af017062783da903b8 -c7a5c46b009c185e4fea8b2eb24c2fa1f9da570fc983358b004f64f07dab69f77c975fbac2bd5933005eeab038987bb302584cdc5a9e8b2c6b87c5f1012c0660 -2d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c -06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1 -012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1ff013c087d0a3c1e809b0000000049454e44ae426082}}{\nonshppict -{\pict\picscalex176\picscaley176\piccropl0\piccropr0\piccropt0\piccropb0\picw2401\pich2401\picwgoal1361\pichgoal1361\wmetafile8\bliptag-78695031\blipupi169{\*\blipuid fb4f3589a5829166a927b41fda22160b} -0100090000037c36000000002719000000001610000026060f002220574d46430100000000000100cbf3000000000200000000200000e8190000e83900000100 -00006c00000000000000000000005a0000005a00000000000000000000001b0b00001b0b000020454d4600000100e83900000c00000001000000000000000000 -000000000000000400000003000040010000f000000000000000000000000000000000e2040080a90300460000002c00000020000000454d462b014001001c00 -0000100000000210c0db0100000060000000600000004600000000060000f4050000454d462b224004000c000000000000001e4009000c000000000000002440 -01000c000000000000003040020010000000040000000000803f214007000c00000000000000084000054c050000400500000210c0db01000000000000000000 -000000000000000000000100000089504e470d0a1a0a0000000d49484452000000a0000000a0080200000004adf07a000000017352474200aece1ce900000009 -7048597300001a0b00001a0b01b149da8a000004c849444154785eed9cdb92e2300c4497fdff7f66bd45d514032438b6a596e4c3c33c4cd96aa98f940b97dcee -f7fb1f5e751df85bb7342afbef00808bf70180015cdc81e2e531c1002eee40f1f29860001777a078794c30808b3b50bc3c2618c0c51d285e1e130ce0e20e142f -8f09067071078a97c70403b8b803c5cb6382015cdc81e2e531c1002eee40f1f29860001777a078794c30808b3b50bc3c2618c0c51d285e1e130ce0e20e142f8f -09067071078a97c70403b8b803c5cb6382015cdc8195e5dd6eb795e156c40a37c1cda3c76b4575ae3162e67c933f65e7c517793ec34dd10a69c93ffe0e0759be -51904d19a22f30f6055c95e847c0ed9fa186d8648283107536fa47ce59f7fca8be067010a24723b5fcc4f631e033d7388c0701c7240ae0f7ceeb02fc7e0310ea -42f168409dc7e845ce59fdc884aefbe086f3e5e573d04365de812ec0933231df01982ceaebf6c73df1d765d60b3c005bd740fc13073c00ab7a59a5fb63b73c81 -96890760ed8445384e0a1da80f58686e93960f717dc0728bb51de60458ebb2567d0bc0da22b5eadaf6729a60adc511ce852a077601ac652c1c623fc0c22255d3 -1341d70f70846a376cb2bd000b9b4cd55bae8055453e738d9083679fb902f62cec446b2bc6c50187622949a638e0a33996782d398079038ee36c9c4c4cc17b03 -362d267e70ffaeda1ab0bfddfe2db83560edfb973eb00580a3cd8d733ece7202c03e9d7b49c5d9f44bb94d2e06f0a48123db3dfb4903d8b3c24eadce65233ca5 -7b3480a5251f8a97640ce05fbcdd18bb0901f875a0ddacf7398cc90047f631726e57db4206f86aa233eb07800d6c99c9d06eef1680c7ecabc1787bc0017ee139 -d67f9dbb9480438cc8e933adfa330cf27bfe77ea4ac09d3da85dd6cf589be791fa2e806738cdec9553df05f0a4d179198b0127322e51aacfdd2c063c3958cedb -333206f0b52649c77823c0e9d85c6bbd83d57ac0e97ccf95b01ef0923e750e92883180077be39971d8b7b15a6d001e04dcb6a598e3aea7cd9e7890e831638d47 -2b64f9b43d1c7804bff45a9ec947f559c0974a3a6f94018f06d42d6c1d8e39bcb1bf700ed1fd5ea55c09e005d8229f8ca3008eec514f0bf89c5f7a3279591305 -b0dbc55af64ebaca781af0a2afbc34dfc30ec1554f43ad9f067cfdf62054fde59399069cd0a1ad0e153b024ed893e3290378dcbb143b019c02d37892001ef72e -c54e00a7c0349e2480c7bd4bb113c029308d2719e5e3c2f10ad879ea00135cbc41000ce0e20e142f8f092e0e78dd45d6a2cf0d8bfbed5ede3ac0eea923d8e300 -87e81e9712af017062783da903b8c7a5c46b009c185e4fea8b2eb24c2fa1f9da570fc983358b004f64f07dab69f77c975fbac2bd5933005eeab038987bb30258 -4cdc5a9e8b2c6b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5 -f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b -87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1012c06602d0f -606b87c5f1012c06602d0f606b87c5f1012c06602d0f606b87c5f1ff013c087d0a3c1e809b0000000049454e44ae426082000840010824000000180000000210 -c0db01000000030000000000000000000000000000001b40000040000000340000000100000002000000000000bf000000bf0000204300002043030000000000 -008000000080ffffb5420000008000000080ffffb5422100000008000000620000000c00000001000000150000000c00000004000000150000000c0000000400 -0000510000008432000000000000000000005a0000005a00000000000000000000000000000000000000a0000000a00000005000000034000000840000000032 -0000000000002000cc005b0000005b00000028000000a0000000a00000000100040000000000000000000000000000000000030000000000000000000000ffff -ff000000ff0011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111122222222222222222222222222222222222222111111111111111111111111111111111111111111111111111111222222222222222222222222222222 -22222222222222222222222222222222222222111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222 -22111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222 -22222222222222222222222222222222222222222222111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111011111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111 -12111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111011111111111111111111 -11111111111111111111111111111111111111111111111111111111100111111111111111111111111111111111101111111111111111111111111111111111 -11111111111011111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111110111 -11111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111110111111 -11111111111111111111111111110111111111111111111111111111101111111111111111101111111111111111111111111111111111111111111111111111 -11111111111111111111111110111111111111111111111111111111111101111111111111111111111111110111111111111111111011111111111111111111 -11111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111011111111111111111111111111101111111 -11111111111011111111111111111111111111111111111111111111111111111000000000000000000000000000000000000000000000000000000000110111 -11111111111111111111111001111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111101111111 -11111111111111111111111111101111111111111111111111111101011111111111111111011111111111111111111111111111111111111111111111111111 -11111111111111111111111101111111111111111111111111111111111011111111111111111111111110110111111111111111110111111111111111111111 -11111111111111111111111111111111111111111111111111111111011111111111111111111111111111111110111111111111111111111111011110111111 -11111111110111111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111101111 -11111111111111111110111110111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111011111111 -11111111111111111111111111101111111111111111111111011111101111111111111111011111111111111111111111111111111111111111111111111111 -11111111111111111111111011111111111111111111111111111111110111111111111111111111110111111011111111111111110111111111111111111111 -11111111111111111111111111111111111111111111111111111110111111111111111111111111111111111101111111111111111111111011111110111111 -11111111110111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111011111 -11111111111111110111111110111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111110111111111 -11111111111111111111111111011111111111111111111011111111101111111111111111011111111111111111111111111111111111111111111111110a0d -000026060f000a1a574d4643010000000000010000000000000002000000e819000000000000e839000011111111111111111111111111011111111111111111 -11111111111111111101111111111111111111011111111110111111111111111101111111111111111111111111111111111111111111111111111111111111 -11111111111111011111111111111111111111111111111110111111111111111111101111111111101111111111111111011111111111111111111111111111 -11111111111111111111111111111111111111111111110111111111111111111111111111111111101111111111111111110111111111111011111111111111 -11011111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111011111112111111 -11101111111111111101111111111111110111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111 -11111111111111111011111111111111111011111111111111011111111111111101111111111111111111111111111111111111111111111111111111111111 -11111111111110111111111111111111111111111111111110111111111111111101111111111111110111111111111111011111111111111111111111111111 -11111111111111111111111111111111111111111111101111111111111111111111111111111111011111111111111110111111111111111101111111111111 -11011111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111110111111111111111 -01111111111111111101111111111111110111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111 -11111111111111110111111111111110111111111111111111011111111111111101111111111111111111111111111111111111111111111111111111111111 -11111111111101111111111111111111111111111111111101111111111111011111111111111111110111111111111110111111111111111111111111111111 -11111111111111111111111111111111111111111111011111111111111111111111111111111111011111111111101111111111111111111101111111111111 -10111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111101111111111110111 -11111111111111111101111111111111101111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111 -11111111111111101111111111110111111111111111111111011111111111110111111111111111111111111111111111111111111111111111111111111111 -11111111111011111111111111111111111111111111111011111111111011111111111111111111110111111111111101111111111111111111111111111111 -11111111111111111111111111111111111111111101111111111111111111111111111111111110111111111101111111111111111111111101111111111110 -11111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111101111111110111111 -11111111111111111101111111111110111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111 -11111111111111101111111101111111111111111111111111011111111111101111111111111111111111111111111111111111111111111111111111111111 -11111111110111111111111111111111111111111111111011111111011111111111111111111111110111111111110111111111111111111111111111111111 -11111111111111111111111111111111111111111011111111111111111111111111111111111110111111101111111111111111111111111101111111111101 -11111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111101111110111111111 -11111111111111111101111111111101111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111 -11111111111111011111110111111111111111111111111111011111111110111111111111111111111111111111111111111111111111111111111111111111 -11111111101111111111111111111111111111111111110111111011111111111111111111111111110111111111101111111111111111111111111111111111 -11111111111111111111111111111111111111110111111111111111111111111111111111111101111101111111111111111111111111111101111111111011 -11111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111011111011111111111 -11111111111111111101111111110111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111 -11111111111111011110111111111111111111111111111111011111111101111111111111111111111111111111111111111111111111111111111111111111 -11111110111111111111111111111111111111111111110111101111111111111111111111111111110111111111011111111111111111111111111111111111 -11111111111111111111111111111111111111101111111111111111111111111111111111111101110111111111111111111111111111111101111111101111 -11111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111011011111111111111 -11111111111111111101111111101111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111 -11111111111111011011111111111111111111111111111111011111110111111111111111111111111111111111111111111111111111111111111111111111 -11111101111111111111111111111111111111111111110101111111111111111111111111111111110111111101111111111111111111111111111111111111 -11111111111111111111111111111111111111011111111111111111111111111111111111111100111111111111111111111111111111111101111111011111 -11111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111001111111111111111 -11111111111111111101111110111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111 -11111111111111011111111111111111111111111111111111011111101111111111111111111111111111111111111111111111111111111111111111111111 -11111011111111111111111111111111111111111111101111111111111111111111111111111111110111110111111111111111111111111111111111111111 -11111111111111111111111111111111111110111111111111111111111111111111111111111011111111111111111111111111111111111101111101111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111 -11111111111111111101111011111111111111111111111111111111111111111111111111111111000000111111111111111111111111111111111111111111 -11111111111110111111111111111111111111111111111111011110111111111111111111111111111111111111111111111111111111111111110000011111 -11111111111111111111111111111111111111111111101111111111111111111111111111111111110111011111111111111111111111111111111111111111 -11111111111111111111111111100000111111111111111111111111111111111111111111111011111111111111111111111111111111111101110111111111 -11111111111111111111111111111111111111111111111111111111111111110000011111111111111111111111111111111111111110111111111111111111 -11111111111111111101101111111111111111111111111111111111111111111111111111111111111111111111111111111000001111111111111111111111 -11111111111111111111111111111111111111111111111111011011111111111111111111111111111111111111111111111111111111111111111111111111 -11111111110000011111111111111111111111111111111111111111111111111111111111111111110101111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111000001111111111111111111111111111111111111111111111111111111111111101011111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111100011111111111111111111111111111111111111111 -11111111111111111100111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111114c00000064000000000000000000 -00005a0000005a00000000000000000000005b0000005b0000002900aa0000000000000000000000803f00000000000000000000803f00000000000000000000 -00000000000000000000000000000000000000000000220000000c000000ffffffff460000001c00000010000000454d462b024000000c000000000000000e00 -0000140000000000000010000000140000000400000003010800050000000b0200000000050000000c025b005b00030000001e00040000000701040004000000 -0701040027190000410b2000cc00a000a000000000005b005b000000000028000000a0000000a000000001000400000000000000000000000000000000000300 -00000000000000000000ffffff000000ff0011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111122222222222222222222222222222222222222111111111111111111111111111111111111111111111111111111222222 -22222222222222222222222222222222222222222222222222222222222222111111111111111111111111111111111111111111111111111111111111122222 -22222222222222222222222222111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222 -22222222222222222222222222222222222222222222222222222222222222222222111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111111111101111111111111111111111111111 -11111111111111111111111112111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111111 -11101111111111111111111111111111111111111111111111111111111111111111111111111111100111111111111111111111111111111111101111111111 -11111111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111 -11111111111111111111011111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111 -11111111111111111011111111111111111111111111111111110111111111111111111111111111101111111111111111101111111111111111111111111111 -11111111111111111111111111111111111111111111111110111111111111111111111111111111111101111111111111111111111111110111111111111111 -11101111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111011111111111 -11111111111111110111111111111111111011111111111111111111111111111111111111111111111111111000000000000000000000000000000000000000 -00000000000000000011011111111111111111111111111001111111111111111101111111111111111111111111111111111111111111111111111111111111 -11111111111111110111111111111111111111111111111111101111111111111111111111111101011111111111111111011111111111111111111111111111 -11111111111111111111111111111111111111111111111101111111111111111111111111111111111011111111111111111111111110110111111111111111 -11011111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111110111111111111 -11111111111101111011111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111 -11111111111111111110111111111111111111111110111110111111111111111101111111111111111111111111111111111111111111111111111111111111 -11111111111111101111111111111111111111111111111111101111111111111111111111011111101111111111111111011111111111111111111111111111 -11111111111111111111111111111111111111111111111011111111111111111111111111111111110111111111111111111111110111111011111111111111 -11011111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111101111111111111 -11111111101111111011111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111 -11111111111111111101111111111111111111110111111110111111111111111101111111111111111111111111111111111111111111111111111111111111 -11111111111111011111111111111111111111111111111111011111111111111111111011111111101111111111111111011111111111111111111111111111 -11111111111111111111111111111111111111111111110111111111111111111111111111111111110111111111111111111101111111111011111111111111 -11011111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111011111111111111 -11111011111111111011111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111 -11111111111111111011111111111111111101111111111110111111111111111101111111111111111111111111111111111111111111111111111111111111 -11111111111110111111111111111111111111111111111110111111121111111110111111111111110111111111111111011111111111111111111111111111 -11111111111111111111111111111111111111111111101111111111111111111111111111111111101111111111111111101111111111111101111111111111 -11011111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111011111111111111 -11011111111111111101111111111111110111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111 -11111111111111110111111111111111101111111111111111011111111111111101111111111111111111111111111111111111111111111111111111111111 -11111111111101111111111111111111111111111111111101111111111111110111111111111111110111111111111111011111111111111111111111111111 -11111111111111111111111111111111111111111111011111111111111111111111111111111111011111111111111011111111111111111101111111111111 -11011111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111110111111111111101 -11111111111111111101111111111111101111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111 -11111111111111110111111111111011111111111111111111011111111111111011111111111111111111111111111111111111111111111111111111111111 -11111111111011111111111111111111111111111111111011111111111101111111111111111111110111111111111110111111111111111111111111111111 -11111111111111111111111111111111111111111110111111111111111111111111111111111110111111111111011111111111111111111101111111111111 -01111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111101111111111101111 -11111111111111111101111111111111011111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111 -11111111111111101111111111011111111111111111111111011111111111101111111111111111111111111111111111111111111111111111111111111111 -11111111110111111111111111111111111111111111111011111111101111111111111111111111110111111111111011111111111111111111111111111111 -11111111111111111111111111111111111111111101111111111111111111111111111111111110111111110111111111111111111111111101111111111110 -11111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111101111111101111111 -11111111111111111101111111111101111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111 -11111111111111101111111011111111111111111111111111011111111111011111111111111111111111111111111111111111111111111111111111111111 -11111111101111111111111111111111111111111111111011111101111111111111111111111111110111111111110111111111111111111111111111111111 -11111111111111111111111111111111111111111011111111111111111111111111111111111101111111011111111111111111111111111101111111111011 -11111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111011111101111111111 -11111111111111111101111111111011111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111 -11111111111111011111011111111111111111111111111111011111111110111111111111111111111111111111111111111111111111111111111111111111 -11111111011111111111111111111111111111111111110111110111111111111111111111111111110111111111011111111111111111111111111111111111 -11111111111111111111111111111111111111110111111111111111111111111111111111111101111011111111111111111111111111111101111111110111 -11111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111011110111111111111 -11111111111111111101111111110111111111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111 -11111111111111011101111111111111111111111111111111011111111011111111111111111111111111111111111111111111111111111111111111111111 -11111110111111111111111111111111111111111111110110111111111111111111111111111111110111111110111111111111111111111111111111111111 -11111111111111111111111111111111111111101111111111111111111111111111111111111101101111111111111111111111111111111101111111011111 -11111111111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111010111111111111111 -11111111111111111101111111011111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111 -11111111111111001111111111111111111111111111111111011111110111111111111111111111111111111111111111111111111111111111111111111111 -11111101111111111111111111111111111111111111110011111111111111111111111111111111110111111011111111111111111111111111111111111111 -11111111111111111111111111111111111111011111111111111111111111111111111111111101111111111111111111111111111111111101111110111111 -11111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111110111111111111111111 -11111111111111111101111101111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111 -11111111111110111111111111111111111111111111111111011111011111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111101111111111111111111111111111111111110111101111111111111111111111111111111111111111 -11111111111111110000001111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111101111011111111 -11111111111111111111111111111111111111111111111111111100000111111111111111111111111111111111111111111111111110111111111111111111 -11111111111111111101110111111111111111111111111111111111111111111111111111111111111111111110000011111111111111111111111111111111 -11111111111110111111111111111111111111111111111111011101111111111111111111111111111111111111111111111111111111111111111111111111 -00000111111111111111111111111111111111111111101111111111111111111111111111111111110110111111111111111111111111111111111111111111 -11111111111111111111111111111111111110000011111111111111111111111111111111111111111111111111111111111111111111111101101111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111000001111111111111111111111111111111111111111111111111 -11111111111111111101011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000111111111111 -11111111111111111111111111111111111111111111111111010111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111000111111111111111111111111111111111111111111111111111111111110011111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110c00000040092900aa000000000000005b005b0000000000040000002701ffff030000000000}}}}}}\sectd -\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid13466995 -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\insrsid13466995 -\par \ltrrow}\trowd \irow0\irowband0\lastrow \ltrrow\ts11\trgaph108\trrh300\trleft-5\trftsWidth3\trwWidth11460\trftsWidthB3\trftsWidthA3\trautofit1\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tblrsid13466995\tblind103\tblindtype3 \clvertalb -\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clNoWrap\clftsWidth3\clwWidth2460\clshdrawnil \cellx2455\clvertalb\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrnone \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 -\cltxlrtb\clNoWrap\clftsWidth3\clwWidth5180\clshdrawnil \cellx7635\clvertalb\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrnone \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clNoWrap\clftsWidth3\clwWidth3820\clshdrawnil \cellx11455\pard \ltrpar -\ql \li0\ri0\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0 {\rtlch\fcs1 \af1 \ltrch\fcs0 \f1\insrsid13466995 EXDS01A1108\cell EXDS01A1COMMON\cell }{\rtlch\fcs1 \af1 \ltrch\fcs0 \f1\cf17\insrsid13466995 EXDS01AT060\cell -}\pard \ltrpar\ql \li0\ri0\widctlpar\intbl\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0 {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \fs20\insrsid13466995 \trowd \irow0\irowband0\lastrow \ltrrow -\ts11\trgaph108\trrh300\trleft-5\trftsWidth3\trwWidth11460\trftsWidthB3\trftsWidthA3\trautofit1\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tblrsid13466995\tblind103\tblindtype3 \clvertalb\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrs\brdrw10 -\clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clNoWrap\clftsWidth3\clwWidth2460\clshdrawnil \cellx2455\clvertalb\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrnone \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 -\cltxlrtb\clNoWrap\clftsWidth3\clwWidth5180\clshdrawnil \cellx7635\clvertalb\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrnone \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrs\brdrw10 \cltxlrtb\clNoWrap\clftsWidth3\clwWidth3820\clshdrawnil \cellx11455\row }\pard \ltrpar -\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid13466995 -\par }} \ No newline at end of file diff --git a/third-party/couchdb-lucene/src/test/resources/example.xml b/third-party/couchdb-lucene/src/test/resources/example.xml deleted file mode 100644 index c2394fc24d..0000000000 --- a/third-party/couchdb-lucene/src/test/resources/example.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - some text - - diff --git a/third-party/couchdb-lucene/src/test/resources/paxos-simple.pdf b/third-party/couchdb-lucene/src/test/resources/paxos-simple.pdf deleted file mode 100644 index 9bce480ddcb4f8c6d0c253dcfc4bb0a66e2ebe4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 95019 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 GC;t!Q6^B>= diff --git a/third-party/pom.xml b/third-party/pom.xml deleted file mode 100644 index 3fa9e14449..0000000000 --- a/third-party/pom.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - 4.0.0 - - - org.eclipse.sw360 - sw360 - 18.99.1 - - - third-party - pom - - - couchdb-lucene - - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.4 - - - - - - maven-dependency-plugin - - - maven-resources-plugin - - - - \ No newline at end of file From ebdb55d4bd4c6c588c77501b67f9f0837493d79b Mon Sep 17 00:00:00 2001 From: Gaurav Mishra Date: Mon, 15 Apr 2024 17:21:58 +0530 Subject: [PATCH 04/10] feat(couchdb): add Dockerfile for couchdb-nouveau 1. Add Dockerfile to build couchdb with nouveau. 2. Add script to install CouchDB with Nouveau. Signed-off-by: Gaurav Mishra --- scripts/couchdb/10-docker-default.ini | 9 ++ scripts/couchdb/Dockerfile | 139 ++++++++++++++++++++++++++ scripts/couchdb/docker-entrypoint.sh | 92 +++++++++++++++++ scripts/couchdb/supervisord.conf | 23 +++++ scripts/couchdb/vm.args | 40 ++++++++ scripts/install-couchdb-nouveau.sh | 107 ++++++++++++++++++++ 6 files changed, 410 insertions(+) create mode 100644 scripts/couchdb/10-docker-default.ini create mode 100644 scripts/couchdb/Dockerfile create mode 100644 scripts/couchdb/docker-entrypoint.sh create mode 100644 scripts/couchdb/supervisord.conf create mode 100644 scripts/couchdb/vm.args create mode 100755 scripts/install-couchdb-nouveau.sh diff --git a/scripts/couchdb/10-docker-default.ini b/scripts/couchdb/10-docker-default.ini new file mode 100644 index 0000000000..d5aa402ae8 --- /dev/null +++ b/scripts/couchdb/10-docker-default.ini @@ -0,0 +1,9 @@ +; SPDX-License-Identifier: EPL-2.0 AND Apache-2.0 +; SPDX-FileCopyrightText: © CouchDB Developers + +; CouchDB Configuration Settings + +; Adopted from https://github.com/apache/couchdb-docker/blob/58910ed097489dc588b2a87592406f8faa1bdadf/3.3.3/10-docker-default.ini + +[chttpd] +bind_address = any diff --git a/scripts/couchdb/Dockerfile b/scripts/couchdb/Dockerfile new file mode 100644 index 0000000000..e12532b6de --- /dev/null +++ b/scripts/couchdb/Dockerfile @@ -0,0 +1,139 @@ +# SPDX-License-Identifier: EPL-2.0 AND Apache-2.0 +# SPDX-FileCopyrightText: © CouchDB Developers , 2024 Siemens AG +# SPDX-FileContributor: Gaurav Mishra +# +# Adopted from https://github.com/apache/couchdb-docker/blob/58910ed097489dc588b2a87592406f8faa1bdadf/3.3.3/Dockerfile + +ARG JAVA_VERSION=17 +ARG UBUNTU_VERSION=jammy + +# Use OpenJDK Eclipe Temurin Ubuntu LTS +FROM eclipse-temurin:$JAVA_VERSION-jdk-$UBUNTU_VERSION as builder + +# Setup erlang repository source to get latest otp compiler +RUN apt-get update -qq \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y curl gpg \ + && mkdir -p /etc/apt/trusted.gpg.d/ /etc/apt/sources.list.d/ \ + && printf "deb [arch=amd64 signed-by=/etc/apt/trusted.gpg.d/erlang.gpg] http://binaries2.erlang-solutions.com/ubuntu/ jammy-esl-erlang-25 contrib" > /etc/apt/sources.list.d/erlang.list \ + && curl -fsSL https://binaries2.erlang-solutions.com/GPG-KEY-pmanager.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/erlang.gpg \ + && printf "deb [arch=amd64 signed-by=/etc/apt/trusted.gpg.d/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/nodesource.gpg \ + && chmod 644 /etc/apt/sources.list.d/*.list /etc/apt/trusted.gpg.d/*.gpg \ + && printf "Package: nsolid\nPin: origin deb.nodesource.com\nPin-Priority: 600\n" > /etc/apt/preferences.d/nsolid \ + && printf "Package: nodejs\nPin: origin deb.nodesource.com\nPin-Priority: 600\n" > /etc/apt/preferences.d/nodejs + +RUN --mount=type=cache,target=/var/cache/apt \ + apt-get update -qq \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + build-essential \ + curl \ + debhelper \ + dh-exec \ + devscripts \ + esl-erlang \ + git \ + help2man \ + libcurl4-openssl-dev \ + libicu-dev \ + libmozjs-78-dev \ + libssl-dev \ + lsb-release \ + nodejs \ + pkgconf \ + python3 \ + python3-venv \ + po-debconf \ + wget \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /couchdb + +# Clone the couchdb-pkg repository and couchdb repository +# The couchdb-pkg repository is used to build the debian packages. +RUN git clone --single-branch --branch main https://github.com/apache/couchdb-pkg.git couchdb-pkg \ + && git clone --single-branch --branch main https://github.com/apache/couchdb.git couchdb + +# Checkout repositories to last known commits +RUN cd couchdb-pkg \ + && git reset --hard 56d6eec5892b7dbdcad02fe709779f222ab12014 \ + && cd ../couchdb \ + && git reset --hard b85d4bb143550ad7227152c3f5e2fc6a6cf98f96 + +# Configure couchdb with nouveau +WORKDIR /couchdb/couchdb + +RUN ./configure --with-nouveau --spidermonkey-version 78 + +# Build the couchdb debian package +WORKDIR /couchdb/couchdb-pkg + +# Build couchdb and cache +RUN make build-couch + +# Build Debian packages +RUN make ubuntu-jammy + +## Final image with JRE +FROM eclipse-temurin:$JAVA_VERSION-jre-$UBUNTU_VERSION + +WORKDIR /app + +# Copy the couchdb debian packages from the builder +COPY --from=builder /couchdb/couchdb/couchdb_*.deb /app/ +COPY --from=builder /couchdb/couchdb/couchdb-nouveau_*.deb /app/ + +# Add CouchDB user account to make sure the IDs are assigned consistently +RUN groupadd -g 5984 -r couchdb && useradd -u 5984 -d /opt/couchdb -g couchdb couchdb + +# Install the couchdb debian packages +# https://github.com/apache/couchdb-pkg/blob/main/debian/README.Debian +RUN --mount=type=cache,target=/var/cache/apt \ + echo "couchdb couchdb/mode select none" | debconf-set-selections \ + && apt-get update -qq \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y --allow-downgrades \ + --allow-remove-essential --allow-change-held-packages \ + /app/couchdb_*.deb \ + /app/couchdb-nouveau_*.deb \ + supervisor \ + || DEBIAN_FRONTEND=noninteractive apt-get install -f \ + && rm -rf /app/* \ + && rm -rf /var/lib/apt/lists/* + +# Undo symlinks to /var/log and /var/lib +RUN \ + rmdir /var/lib/couchdb /var/log/couchdb; \ + rm /opt/couchdb/data /opt/couchdb/var/log; \ + mkdir -p /opt/couchdb/data /opt/couchdb/var/log; \ + chown couchdb:couchdb /opt/couchdb/data /opt/couchdb/var/log; \ + chmod 777 /opt/couchdb/data /opt/couchdb/var/log; \ +# Remove file that sets logging to a file + rm /opt/couchdb/etc/default.d/10-filelog.ini; \ +# Check we own everything in /opt/couchdb. Matches the command in dockerfile_entrypoint.sh + find /opt/couchdb \! \( -user couchdb -group couchdb \) -exec chown -f couchdb:couchdb '{}' +; \ +# Setup directories and permissions for config. Technically these could be 555 and 444 respectively +# but we keep them as 755 and 644 for consistency with CouchDB defaults and the dockerfile_entrypoint.sh. + find /opt/couchdb/etc -type d ! -perm 0755 -exec chmod -f 0755 '{}' +; \ + find /opt/couchdb/etc -type f ! -perm 0644 -exec chmod -f 0644 '{}' +; \ +# only local.d needs to be writable for the docker_entrypoint.sh + chmod -f 0777 /opt/couchdb/etc/local.d; \ +# Make nouveau root directory as absolute path + sed -i "s,rootDir:.*,rootDir: /opt/couchdb/data/nouveau,g" /opt/couchdb/etc/nouveau.yaml + +# Add configuration +COPY --chown=couchdb:couchdb 10-docker-default.ini /opt/couchdb/etc/default.d/ +COPY --chown=couchdb:couchdb vm.args /opt/couchdb/etc/ +COPY docker-entrypoint.sh /usr/local/bin +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf + +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +ENTRYPOINT ["/usr/bin/supervisord"] + +VOLUME /opt/couchdb/data + +# 5984: Main CouchDB endpoint +# 4369: Erlang portmap daemon (epmd) +# 9100: CouchDB cluster communication port +EXPOSE 5984 4369 9100 + +HEALTHCHECK --interval=10s --timeout=5s --retries=5 CMD curl --fail -s http://localhost:5984/_up diff --git a/scripts/couchdb/docker-entrypoint.sh b/scripts/couchdb/docker-entrypoint.sh new file mode 100644 index 0000000000..b6bf4ca32a --- /dev/null +++ b/scripts/couchdb/docker-entrypoint.sh @@ -0,0 +1,92 @@ +#!/bin/bash +# SPDX-License-Identifier: EPL-2.0 +# SPDX-FileCopyrightText: © CouchDB Developers , 2024 Siemens AG +# +# Adopted from https://github.com/apache/couchdb-docker/blob/58910ed097489dc588b2a87592406f8faa1bdadf/3.3.3/docker-entrypoint.sh + +set -em + +# first arg is `-something` or `+something` +if [ "${1#-}" != "$1" ] || [ "${1#+}" != "$1" ]; then + set -- /opt/couchdb/bin/couchdb "$@" +fi + +# first arg is the bare word `couchdb` +if [ "$1" = 'couchdb' ]; then + shift + set -- /opt/couchdb/bin/couchdb "$@" +fi + +if [ "$1" = '/opt/couchdb/bin/couchdb' ]; then + touch /opt/couchdb/etc/local.d/docker.ini + + if [ "$(id -u)" = '0' ]; then + # Fix file permissions + find /opt/couchdb \! \( -user couchdb -group couchdb \) -exec chown -f couchdb:couchdb '{}' + + find /opt/couchdb/data -type d ! -perm 0755 -exec chmod -f 0755 '{}' + + find /opt/couchdb/data -type f ! -perm 0644 -exec chmod -f 0644 '{}' + + find /opt/couchdb/etc -type d ! -perm 0755 -exec chmod -f 0755 '{}' + + find /opt/couchdb/etc -type f ! -perm 0644 -exec chmod -f 0644 '{}' + + fi + + if [ ! -z "$NODENAME" ] && ! grep "couchdb@" /opt/couchdb/etc/vm.args; then + echo "-name couchdb@$NODENAME" >> /opt/couchdb/etc/vm.args + fi + + if [ "$COUCHDB_USER" ] && [ "$COUCHDB_PASSWORD" ]; then + # Create admin only if not already present + if ! grep -Pzoqr "\[admins\]\n$COUCHDB_USER =" /opt/couchdb/etc/local.d/*.ini /opt/couchdb/etc/local.ini; then + printf "\n[admins]\n%s = %s\n" "$COUCHDB_USER" "$COUCHDB_PASSWORD" >> /opt/couchdb/etc/local.d/docker.ini + fi + fi + + if [ "$COUCHDB_SECRET" ]; then + # Set secret only if not already present + if ! grep -Pzoqr "\[chttpd_auth\]\nsecret =" /opt/couchdb/etc/local.d/*.ini /opt/couchdb/etc/local.ini; then + printf "\n[chttpd_auth]\nsecret = %s\n" "$COUCHDB_SECRET" >> /opt/couchdb/etc/local.d/docker.ini + fi + fi + + if [ "$COUCHDB_ERLANG_COOKIE" ]; then + cookieFile='/opt/couchdb/.erlang.cookie' + if [ -e "$cookieFile" ]; then + if [ "$(cat "$cookieFile" 2>/dev/null)" != "$COUCHDB_ERLANG_COOKIE" ]; then + echo >&2 + echo >&2 "warning: $cookieFile contents do not match COUCHDB_ERLANG_COOKIE" + echo >&2 + fi + else + echo "$COUCHDB_ERLANG_COOKIE" > "$cookieFile" + fi + chown couchdb:couchdb "$cookieFile" + chmod 600 "$cookieFile" + fi + + if [ "$(id -u)" = '0' ]; then + chown -f couchdb:couchdb /opt/couchdb/etc/local.d/docker.ini || true + fi + + # if we don't find an [admins] section followed by a non-comment, display a warning + if ! grep -Pzoqr '\[admins\]\n[^;]\w+' /opt/couchdb/etc/default.d/*.ini /opt/couchdb/etc/local.d/*.ini /opt/couchdb/etc/local.ini; then + # The - option suppresses leading tabs but *not* spaces. :) + cat >&2 <<-'EOWARN' +************************************************************* +ERROR: CouchDB 3.0+ will no longer run in "Admin Party" + mode. You *MUST* specify an admin user and + password, either via your own .ini file mapped + into the container at /opt/couchdb/etc/local.ini + or inside /opt/couchdb/etc/local.d, or with + "-e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password" + to set it via "docker run". +************************************************************* +EOWARN + exit 1 + fi + + if [ "$(id -u)" = '0' ]; then + export HOME=$(echo ~couchdb) + exec setpriv --reuid=couchdb --regid=couchdb --clear-groups "$@" + fi +fi + +exec "$@" diff --git a/scripts/couchdb/supervisord.conf b/scripts/couchdb/supervisord.conf new file mode 100644 index 0000000000..20587095ea --- /dev/null +++ b/scripts/couchdb/supervisord.conf @@ -0,0 +1,23 @@ +; SPDX-License-Identifier: EPL-2.0 +; SPDX-FileCopyrightText: © 2024 Siemens AG +; SPDX-FileContributor: Gaurav Mishra + +[supervisord] +nodaemon=true +logfile=/dev/null +logfile_maxbytes=0 + +[program:couchdb] +command=/usr/local/bin/docker-entrypoint.sh /opt/couchdb/bin/couchdb +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true + +[program:nouveau] +command=/opt/couchdb/nouveau/bin/nouveau server /opt/couchdb/etc/nouveau.yaml +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true + +[group:couchdb] +programs=couchdb,nouveau diff --git a/scripts/couchdb/vm.args b/scripts/couchdb/vm.args new file mode 100644 index 0000000000..c926d17ffc --- /dev/null +++ b/scripts/couchdb/vm.args @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: EPL-2.0 AND Apache-2.0 +# SPDX-FileCopyrightText: © CouchDB Developers +# +# Adopted from https://github.com/apache/couchdb-docker/blob/58910ed097489dc588b2a87592406f8faa1bdadf/3.3.3/vm.args + +# Ensure that the Erlang VM listens on a known port +-kernel inet_dist_listen_min 9100 +-kernel inet_dist_listen_max 9100 + +# Tell kernel and SASL not to log anything +-kernel error_logger silent +-sasl sasl_error_logger false + +# This will toggle to true in Erlang 25+. However since we don't use global +# any longer, and have our own auto-connection module, we can keep the +# existing global behavior to avoid surprises. See +# https://github.com/erlang/otp/issues/6470#issuecomment-1337421210 for more +# information about possible increased coordination and messages being sent on +# disconnections when this setting is enabled. +# +-kernel prevent_overlapping_partitions false + +# Increase the pool of dirty IO schedulers from 10 to 16 +# Dirty IO schedulers are used for file IO. ++SDio 16 + +# Increase distribution buffer size from default of 1MB to 32MB. The default is +# usually a bit low on busy clusters. Has no effect for single-node setups. +# The unit is in kilobytes. ++zdbbl 32768 + +# When running on Docker, Kubernetes or an OS using CFS (Completely Fair +# Scheduler) with CPU quota limits set, disable busy waiting for schedulers to +# avoid busy waiting consuming too much of Erlang VM's CPU time-slice shares. ++sbwt none ++sbwtdcpu none ++sbwtdio none + +# Comment this line out to enable the interactive Erlang shell on startup ++Bd -noinput diff --git a/scripts/install-couchdb-nouveau.sh b/scripts/install-couchdb-nouveau.sh new file mode 100755 index 0000000000..f4053f6b43 --- /dev/null +++ b/scripts/install-couchdb-nouveau.sh @@ -0,0 +1,107 @@ +#!/bin/bash +# ----------------------------------------------------------------------------- +# Copyright Siemens AG, 2024. +# Part of the SW360 Portal Project. +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# script to build couchdb server with nouveau plugin enabled. +# +# +# initial author: mishra.gaurav@siemens.com +# ----------------------------------------------------------------------------- + +set -e +set -x + +BASEDIR="${BASEDIR:-/tmp}" +NOUVEAU_PATH="/opt/couchdb/lib/nouveau-*" + +nouveauExists() { ls $NOUVEAU_PATH 1> /dev/null 2>&1; } + +has() { type "$1" &> /dev/null; } + +processCouchdb() { + MOZ_PACKAGE_NAME="libmozjs-*-dev" + + MOZ_PACKAGE=$(dpkg-query -W -f='${Package}\n' $MOZ_PACKAGE_NAME | head -n 1) + + echo "-[shell provisioning] Installing build dependencies" + + $SUDO_CMD DEBIAN_FRONTEND=noninteractive apt-get install -y \ + $MOZ_PACKAGE \ + build-essential \ + curl \ + debhelper \ + dh-exec \ + devscripts \ + erlang-dev \ + erlang-reltool \ + erlang-src \ + erlang \ + git \ + help2man \ + libcurl4-openssl-dev \ + libicu-dev \ + libssl-dev \ + lsb-release \ + pkgconf \ + python3 \ + python3-venv \ + po-debconf \ + wget + + echo "-[shell provisioning] Cloning couchdb" + [ -d "$BASEDIR/couchdb" ] && rm -rf "$BASEDIR/couchdb" + mkdir -p "$BASEDIR/couchdb" + git clone --single-branch --branch main --depth 1 https://github.com/apache/couchdb-pkg.git "$BASEDIR/couchdb/couchdb-pkg" + git clone --single-branch --branch main --depth 1 https://github.com/apache/couchdb.git "$BASEDIR/couchdb/couchdb" + + # Get the version of the package + MOZ_PACKAGE_VERSION=$(dpkg-query -W -f='${Version}\n' $MOZ_PACKAGE_NAME | head -n 1) + # Extract the major version + MOZ_VERSION=$(echo $MOZ_PACKAGE_VERSION | awk -F'.' '{print $1}') + + pushd "$BASEDIR/couchdb/couchdb" + echo "-[shell provisioning] Configuring couchdb with nouveau plugin enabled" + ./configure --enable-nouveau --spidermonkey-version $MOZ_VERSION + popd + + pushd "$BASEDIR/couchdb/couchdb-pkg" + echo "-[shell provisioning] Building couchdb packages" + make build-couch + make "$DISTRO-$CODENAME" + popd + + echo "-[shell provisioning] Installing couchdb" + $SUDO_CMD dpkg -i "$BASEDIR/couchdb/couchdb/couchdb_*.deb" || $SUDO_CMD apt-get --fix-broken install -y +} + +installCouchdb() { + if nouveauExists; then + echo "couchdb with nouveau is already installed" + exit 1 + fi + + processCouchdb +} + +for arg in "$@" +do + echo "Unsupported parameter: $arg" + echo "Usage: $0" + exit 1 +done + +SUDO_CMD="" +if [ "$EUID" -ne 0 ]; then + if has "sudo" ; then + SUDO_CMD="sudo " + fi +fi + +installCouchdb From 81e05d29315a6fb6982dd841a1f58e1fa4747e85 Mon Sep 17 00:00:00 2001 From: Gaurav Mishra Date: Mon, 29 Jul 2024 20:53:15 +0530 Subject: [PATCH 05/10] feat(cloudant): use IBM SDK Use IBM SDK Cloudant and remove ektorp and old Cloudant library. Signed-off-by: Gaurav Mishra --- backend/src-common/pom.xml | 5 + .../db/AttachmentAwareDatabaseHandler.java | 7 +- .../db/AttachmentContentRepository.java | 14 +- .../db/AttachmentDatabaseHandler.java | 40 +- .../db/AttachmentOwnerRepository.java | 4 +- .../datahandler/db/AttachmentRepository.java | 4 +- .../db/AttachmentUsageRepository.java | 4 +- .../db/ChangeLogsDatabaseHandler.java | 7 +- .../datahandler/db/ChangeLogsRepository.java | 4 +- .../db/ComponentDatabaseHandler.java | 45 +- .../datahandler/db/ComponentRepository.java | 4 +- .../db/ComponentSearchHandler.java | 10 +- .../db/ConfigContainerRepository.java | 4 +- .../db/CustomPropertiesRepository.java | 4 +- .../db/ModerationSearchHandler.java | 12 +- .../db/ObligationElementSearchHandler.java | 9 +- .../db/ObligationListRepository.java | 4 +- .../db/ObligationSearchHandler.java | 10 +- .../db/PackageDatabaseHandler.java | 34 +- .../datahandler/db/PackageRepository.java | 4 +- .../datahandler/db/PackageSearchHandler.java | 12 +- .../db/ProjectDatabaseHandler.java | 37 +- .../datahandler/db/ProjectRepository.java | 4 +- .../datahandler/db/ProjectSearchHandler.java | 12 +- .../ProjectVulnerabilityRatingRepository.java | 4 +- .../db/RelationsUsageRepository.java | 4 +- .../datahandler/db/ReleaseRepository.java | 4 +- .../datahandler/db/ReleaseSearchHandler.java | 10 +- .../sw360/datahandler/db/UserRepository.java | 4 +- .../datahandler/db/UserSearchHandler.java | 12 +- .../datahandler/db/VendorRepository.java | 4 +- .../datahandler/db/VendorSearchHandler.java | 13 +- .../document/SpdxDocumentDatabaseHandler.java | 15 +- .../spdx/document/SpdxDocumentRepository.java | 4 +- .../document/SpdxDocumentSearchHandler.java | 12 +- ...dxDocumentCreationInfoDatabaseHandler.java | 9 +- .../SpdxDocumentCreationInfoRepository.java | 4 +- ...SpdxDocumentCreationInfoSearchHandler.java | 12 +- .../SpdxPackageInfoDatabaseHandler.java | 12 +- .../SpdxPackageInfoRepository.java | 4 +- .../SpdxPackageInfoSearchHandler.java | 12 +- .../sw360/attachments/AttachmentHandler.java | 7 +- .../sw360/changelogs/ChangeLogsHandler.java | 6 +- .../sw360/components/ComponentHandler.java | 7 +- .../components/db/BulkDeleteUtilTest.java | 1 - .../db/ComponentDatabaseHandlerTest.java | 1 - .../db/ComponentSearchHandlerTest.java | 2 - .../db/ProjectDatabaseHandlerTest.java | 6 - .../datasink/VulnerabilityConnector.java | 12 +- .../datasink/VulnerabilityConnectorTest.java | 2 +- .../eclipse/sw360/health/HealthHandler.java | 9 +- .../health/db/HealthDatabaseHandler.java | 15 +- .../sw360/licenses/LicenseHandler.java | 5 +- .../licenses/db/LicenseDatabaseHandler.java | 27 +- .../db/LicenseObligationListRepository.java | 4 +- .../sw360/licenses/db/LicenseRepository.java | 4 +- .../licenses/db/LicenseTypeRepository.java | 4 +- .../db/ObligationElementRepository.java | 4 +- .../licenses/db/ObligationNodeRepository.java | 4 +- .../sw360/licenses/db/TodoRepository.java | 4 +- .../db/ClearingRequestRepository.java | 5 +- .../db/LicenseModerationRequestGenerator.java | 6 +- .../db/ModerationDatabaseHandler.java | 27 +- .../db/ModerationRequestRepository.java | 4 +- .../testutil/DatabaseTestSetup.java | 1 - .../sw360/projects/ProjectHandler.java | 13 +- .../sw360/projects/ProjectHandlerTest.java | 1 - .../eclipse/sw360/search/SearchHandler.java | 7 +- .../Sw360usersDatabaseSearchHandler.java | 5 +- .../db/AbstractDatabaseSearchHandler.java | 20 +- .../db/Sw360dbDatabaseSearchHandler.java | 9 +- .../spdxdocument/SPDXDocumentHandler.java | 7 +- .../DocumentCreationInformationHandler.java | 7 +- .../PackageInformationHandler.java | 7 +- .../org/eclipse/sw360/users/UserHandler.java | 5 +- .../sw360/users/db/UserDatabaseHandler.java | 10 +- .../sw360/vendors/VendorDatabaseHandler.java | 7 +- .../eclipse/sw360/vendors/VendorHandler.java | 15 +- .../sw360/vendors/TestVendorClient.java | 1 - .../sw360/vendors/VendorHandlerTest.java | 2 - .../vmcomponents/db/VMActionRepository.java | 1 - .../db/VMComponentRepository.java | 1 - .../vmcomponents/db/VMDatabaseHandler.java | 13 +- .../vmcomponents/db/VMMatchRepository.java | 1 - .../vmcomponents/db/VMPriorityRepository.java | 1 - .../db/VMProcessReportingRepository.java | 1 - .../db/VMDatabaseHandlerTest.java | 1 - .../db/VulnerabilityDatabaseHandler.java | 12 +- .../db/VulnerabilityRelationRepository.java | 4 +- .../db/VulnerabilityRepository.java | 4 +- .../sw360/SW360ServiceContextListener.java | 6 +- .../db/RemoteAttachmentDownloader.java | 9 +- .../db/RemoteAttachmentDownloaderTest.java | 2 - libraries/datahandler/pom.xml | 13 +- .../DatabaseConnectorCloudant.java | 421 +++++++++++++----- .../DatabaseInstanceCloudant.java | 66 ++- .../DatabaseRepositoryCloudantClient.java | 61 ++- .../sw360/datahandler/common/CommonUtils.java | 3 +- .../datahandler/common/DatabaseSettings.java | 55 +-- .../couchdb/AttachmentConnector.java | 11 +- .../couchdb/AttachmentContentWrapper.java | 44 -- .../couchdb/AttachmentStreamConnector.java | 4 +- .../couchdb/DatabaseConnector.java | 276 ------------ .../datahandler/couchdb/DatabaseInstance.java | 44 -- .../couchdb/DatabaseInstanceTracker.java | 31 -- .../couchdb/DatabaseRepository.java | 273 ------------ .../datahandler/couchdb/DocumentWrapper.java | 17 - .../datahandler/couchdb/MapperFactory.java | 101 ----- .../NouveauLuceneAwareDatabaseConnector.java | 34 +- .../sw360/datahandler/thrift/ThriftUtils.java | 10 +- .../common/DatabaseSettingsTest.java | 55 +-- .../couchdb/AttachmentConnectorTest.java | 6 +- libraries/nouveau-handler/pom.xml | 5 + .../nouveau/LuceneAwareCouchDbConnector.java | 64 ++- .../designdocument/NouveauDesignDocument.java | 4 +- pom.xml | 18 +- rest/authorization-server/pom.xml | 11 - 117 files changed, 860 insertions(+), 1539 deletions(-) delete mode 100644 libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentContentWrapper.java delete mode 100644 libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseConnector.java delete mode 100644 libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseInstance.java delete mode 100644 libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseInstanceTracker.java delete mode 100644 libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseRepository.java delete mode 100644 libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DocumentWrapper.java delete mode 100644 libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/MapperFactory.java diff --git a/backend/src-common/pom.xml b/backend/src-common/pom.xml index f439d5ee9e..bae6c27950 100644 --- a/backend/src-common/pom.xml +++ b/backend/src-common/pom.xml @@ -69,5 +69,10 @@ ${project.version} compile
+ + com.ibm.cloud + cloudant + ${cloudantsdk.version} +
diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentAwareDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentAwareDatabaseHandler.java index 34c50559a9..793887ce5d 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentAwareDatabaseHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentAwareDatabaseHandler.java @@ -16,9 +16,9 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; +import com.ibm.cloud.cloudant.v1.Cloudant; import org.apache.thrift.TBase; import org.apache.thrift.TFieldIdEnum; import org.eclipse.sw360.datahandler.common.SW360Utils; @@ -31,7 +31,6 @@ import org.eclipse.sw360.datahandler.thrift.packages.Package; import org.eclipse.sw360.datahandler.thrift.projects.Project; -import com.cloudant.client.api.CloudantClient; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; @@ -43,8 +42,8 @@ protected AttachmentAwareDatabaseHandler(AttachmentDatabaseHandler attachmentDat this.attachmentDatabaseHandler = attachmentDatabaseHandler; } - protected AttachmentAwareDatabaseHandler(Supplier httpClient, String dbName, String attachmentDbName) throws MalformedURLException { - this(new AttachmentDatabaseHandler(httpClient, dbName, attachmentDbName)); + protected AttachmentAwareDatabaseHandler(Cloudant client, String dbName, String attachmentDbName) throws MalformedURLException { + this(new AttachmentDatabaseHandler(client, dbName, attachmentDbName)); } protected Source toSource(Release release){ diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentContentRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentContentRepository.java index 35ae1db878..3204d21087 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentContentRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentContentRepository.java @@ -18,8 +18,8 @@ import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; import org.eclipse.sw360.datahandler.thrift.users.User; -import com.cloudant.client.api.model.Response; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DocumentResult; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import com.cloudant.client.api.views.Key; import com.cloudant.client.api.views.UnpaginatedRequestBuilder; import com.cloudant.client.api.views.ViewRequestBuilder; @@ -44,7 +44,7 @@ public class AttachmentContentRepository extends DatabaseRepositoryCloudantClien public AttachmentContentRepository(DatabaseConnectorCloudant db) { super(db, AttachmentContent.class); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("onlyRemotes", createMapReduce(ONLYREMOTES, null)); views.put("all", createMapReduce(ALL, null)); initStandardDesignDocument(views, db); @@ -69,10 +69,14 @@ public RequestSummary vacuumAttachmentDB(User user, final Set usedIds) { requestSummary.setTotalElements(allAttachmentContents.size()); requestSummary.setTotalAffectedElements(unusedAttachmentContents.size()); - final List documentOperationResults = getConnector().deleteBulk(unusedAttachmentContents); + final List documentOperationResults = getConnector().deleteIds( + unusedAttachmentContents + .stream() + .map(AttachmentContent::getId).collect(Collectors.toSet()) + ); if (unusedAttachmentContents.isEmpty() || !documentOperationResults.isEmpty()) { requestSummary.setRequestStatus(RequestStatus.SUCCESS); - }else{ + } else { requestSummary.setRequestStatus(RequestStatus.FAILURE); } return requestSummary; diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentDatabaseHandler.java index 7bd083d5c2..43c02e551a 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentDatabaseHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentDatabaseHandler.java @@ -9,13 +9,13 @@ */ package org.eclipse.sw360.datahandler.db; -import com.cloudant.client.api.CloudantClient; -import com.cloudant.client.api.model.Response; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; +import com.ibm.cloud.cloudant.v1.Cloudant; +import com.ibm.cloud.cloudant.v1.model.DocumentResult; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.couchdb.AttachmentConnector; @@ -24,7 +24,6 @@ import org.eclipse.sw360.datahandler.thrift.users.User; import org.apache.logging.log4j.Logger; -import org.apache.http.HttpStatus; import org.apache.logging.log4j.LogManager; import org.apache.thrift.TException; @@ -33,7 +32,6 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; import java.util.stream.Collectors; import static org.eclipse.sw360.datahandler.common.Duration.durationOf; @@ -56,13 +54,13 @@ public class AttachmentDatabaseHandler { private static final Logger log = LogManager.getLogger(AttachmentDatabaseHandler.class); - public AttachmentDatabaseHandler(Supplier httpClient, String dbName, String attachmentDbName) throws MalformedURLException { - db = new DatabaseConnectorCloudant(httpClient, attachmentDbName); - attachmentConnector = new AttachmentConnector(httpClient, attachmentDbName, durationOf(30, TimeUnit.SECONDS)); + public AttachmentDatabaseHandler(Cloudant client, String dbName, String attachmentDbName) throws MalformedURLException { + db = new DatabaseConnectorCloudant(client, attachmentDbName); + attachmentConnector = new AttachmentConnector(client, attachmentDbName, durationOf(30, TimeUnit.SECONDS)); attachmentContentRepository = new AttachmentContentRepository(db); - attachmentUsageRepository = new AttachmentUsageRepository(new DatabaseConnectorCloudant(httpClient, dbName)); - attachmentRepository = new AttachmentRepository(new DatabaseConnectorCloudant(httpClient, dbName)); - attachmentOwnerRepository = new AttachmentOwnerRepository(new DatabaseConnectorCloudant(httpClient, dbName)); + attachmentUsageRepository = new AttachmentUsageRepository(new DatabaseConnectorCloudant(client, dbName)); + attachmentRepository = new AttachmentRepository(new DatabaseConnectorCloudant(client, dbName)); + attachmentOwnerRepository = new AttachmentOwnerRepository(new DatabaseConnectorCloudant(client, dbName)); } public AttachmentConnector getAttachmentConnector(){ @@ -74,7 +72,7 @@ public AttachmentContent add(AttachmentContent attachmentContent){ return attachmentContent; } public List makeAttachmentContents(List attachmentContents) throws TException { - final List documentOperationResults = attachmentContentRepository.executeBulk(attachmentContents); + final List documentOperationResults = attachmentContentRepository.executeBulk(attachmentContents); if (documentOperationResults.isEmpty()) log.error("Failed Attachment store results " + documentOperationResults); @@ -91,7 +89,7 @@ public void updateAttachmentContent(AttachmentContent attachment) throws TExcept attachmentConnector.updateAttachmentContent(attachment); } public RequestSummary bulkDelete(List ids) { - final List documentOperationResults = attachmentContentRepository.deleteIds(ids); + final List documentOperationResults = attachmentContentRepository.deleteIds(ids); return CommonUtils.getRequestSummary(ids, documentOperationResults); } public RequestStatus deleteAttachmentContent(String attachmentId) throws TException { @@ -147,9 +145,9 @@ public AttachmentUsage makeAttachmentUsage(AttachmentUsage attachmentUsage) { public void makeAttachmentUsages(List attachmentUsages) throws TException { List sanitizedUsages = distinctAttachmentUsages(attachmentUsages); - List results = attachmentUsageRepository.executeBulk(sanitizedUsages); - results = results.stream().filter(res -> res.getError() != null || res.getStatusCode() != HttpStatus.SC_CREATED) - .collect(Collectors.toList()); + List results = attachmentUsageRepository.executeBulk(sanitizedUsages); + results = results.stream().filter(res -> res.getError() != null || !res.isOk()) + .toList(); if (!results.isEmpty()) { throw new SW360Exception("Some of the usage documents could not be created: " + results); } @@ -187,9 +185,9 @@ public AttachmentUsage updateAttachmentUsage(AttachmentUsage attachmentUsage) { } public void updateAttachmentUsages(List attachmentUsages) throws TException { - List results = attachmentUsageRepository.executeBulk(attachmentUsages); - results = results.stream().filter(res -> res.getError() != null || res.getStatusCode() != HttpStatus.SC_CREATED) - .collect(Collectors.toList()); + List results = attachmentUsageRepository.executeBulk(attachmentUsages); + results = results.stream().filter(res -> res.getError() != null || !res.isOk()) + .toList(); if (!results.isEmpty()) { throw new SW360Exception("Some of the usage documents could not be updated: " + results); } @@ -200,10 +198,10 @@ public void deleteAttachmentUsage(AttachmentUsage attachmentUsage) throws SW360E } public void deleteAttachmentUsages(List attachmentUsages) throws SW360Exception { - List results = attachmentUsageRepository.deleteIds( + List results = attachmentUsageRepository.deleteIds( attachmentUsages.stream().map(AttachmentUsage::getId).collect(Collectors.toList())); - results = results.stream().filter(res -> res.getError() != null || res.getStatusCode() != HttpStatus.SC_OK) - .collect(Collectors.toList()); + results = results.stream().filter(res -> res.getError() != null || !res.isOk()) + .toList(); if (!results.isEmpty()) { throw new SW360Exception("Some of the usage documents could not be deleted: " + results); } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentOwnerRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentOwnerRepository.java index 5717b5ab76..0b530e01fd 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentOwnerRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentOwnerRepository.java @@ -13,7 +13,7 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.Source; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import com.cloudant.client.api.views.ViewRequestBuilder; import java.util.HashMap; @@ -31,7 +31,7 @@ public class AttachmentOwnerRepository extends DatabaseRepositoryCloudantClient< public AttachmentOwnerRepository(DatabaseConnectorCloudant db) { super(db, Source.class); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("attachmentOwner", createMapReduce(ATTACHMENTOWNER_VIEW_NAME, null)); initStandardDesignDocument(views, db); } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentRepository.java index f3b9322c83..2e91da77b1 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentRepository.java @@ -13,7 +13,7 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import com.cloudant.client.api.views.ViewRequestBuilder; import java.util.HashMap; @@ -32,7 +32,7 @@ public class AttachmentRepository extends DatabaseRepositoryCloudantClient views = new HashMap(); + Map views = new HashMap<>(); views.put("byid", createMapReduce(BYID_VIEW_NAME, null)); views.put("bysha1", createMapReduce(BYSHA1_VIEW_NAME, null)); initStandardDesignDocument(views, db); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java index 05efea31c8..ccde27d9b5 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java @@ -10,7 +10,7 @@ package org.eclipse.sw360.datahandler.db; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import com.cloudant.client.api.views.Key; import com.cloudant.client.api.views.MultipleRequestBuilder; import com.cloudant.client.api.views.UnpaginatedRequestBuilder; @@ -54,7 +54,7 @@ public class AttachmentUsageRepository extends DatabaseRepositoryCloudantClient< public AttachmentUsageRepository(DatabaseConnectorCloudant db) { super(db, AttachmentUsage.class); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); views.put("usagesByAttachment", createMapReduce(USAGESBYATTACHMENT, "_count")); views.put("usedAttachments", createMapReduce(USEDATTACHMENTS, "_count")); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ChangeLogsDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ChangeLogsDatabaseHandler.java index 13eb4b284c..53233a8329 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ChangeLogsDatabaseHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ChangeLogsDatabaseHandler.java @@ -17,7 +17,6 @@ import java.util.List; import java.util.Objects; import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; @@ -29,7 +28,7 @@ import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.RequestStatus; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.common.collect.ImmutableSet; /** @@ -48,8 +47,8 @@ public class ChangeLogsDatabaseHandler { .add("revision") .add("documentState").build(); - public ChangeLogsDatabaseHandler(Supplier httpClient, String dbName) throws MalformedURLException { - db = new DatabaseConnectorCloudant(httpClient, dbName); + public ChangeLogsDatabaseHandler(Cloudant client, String dbName) throws MalformedURLException { + db = new DatabaseConnectorCloudant(client, dbName); changeLogsRepository = new ChangeLogsRepository(db); } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ChangeLogsRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ChangeLogsRepository.java index 8d06b06544..f5adaa9164 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ChangeLogsRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ChangeLogsRepository.java @@ -18,7 +18,7 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.changelogs.ChangeLogs; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; /** * CRUD access for the ChangeLogs class @@ -55,7 +55,7 @@ public class ChangeLogsRepository extends DatabaseRepositoryCloudantClient views = new HashMap(); + Map views = new HashMap<>(); views.put("byDocumentId", createMapReduce(BY_DOCUMENT_ID, null)); views.put("byParentDocumentId", createMapReduce(BY_PARENT_DOCUMENT_ID, null)); views.put("byUserEdited", createMapReduce(BY_USER_EDITED, null)); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java index 9b1f2af83d..7512910e77 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java @@ -10,8 +10,8 @@ */ package org.eclipse.sw360.datahandler.db; -import com.cloudant.client.api.CloudantClient; -import com.cloudant.client.api.model.Response; +import com.ibm.cloud.cloudant.v1.Cloudant; +import com.ibm.cloud.cloudant.v1.model.DocumentResult; import com.google.common.collect.*; import org.eclipse.sw360.common.utils.BackendUtils; @@ -83,7 +83,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.Supplier; import java.util.stream.*; import static com.google.common.base.Strings.isNullOrEmpty; @@ -165,9 +164,9 @@ public class ComponentDatabaseHandler extends AttachmentAwareDatabaseHandler { ClearingInformation._Fields.EXTERNAL_SUPPLIER_ID, ClearingInformation._Fields.EVALUATED, ClearingInformation._Fields.PROC_START); - public ComponentDatabaseHandler(Supplier httpClient, String dbName, String attachmentDbName, ComponentModerator moderator, ReleaseModerator releaseModerator, ProjectModerator projectModerator) throws MalformedURLException { - super(httpClient, dbName, attachmentDbName); - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(httpClient, dbName); + public ComponentDatabaseHandler(Cloudant client, String dbName, String attachmentDbName, ComponentModerator moderator, ReleaseModerator releaseModerator, ProjectModerator projectModerator) throws MalformedURLException { + super(client, dbName, attachmentDbName); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); // Create the repositories vendorRepository = new VendorRepository(db); @@ -183,36 +182,36 @@ public ComponentDatabaseHandler(Supplier httpClient, String dbNa this.projectModerator = projectModerator; // Create the attachment connector - attachmentConnector = new AttachmentConnector(httpClient, attachmentDbName, durationOf(30, TimeUnit.SECONDS)); - DatabaseConnectorCloudant dbChangeLogs = new DatabaseConnectorCloudant(httpClient, DatabaseSettings.COUCH_DB_CHANGE_LOGS); + attachmentConnector = new AttachmentConnector(client, attachmentDbName, durationOf(30, TimeUnit.SECONDS)); + DatabaseConnectorCloudant dbChangeLogs = new DatabaseConnectorCloudant(client, DatabaseSettings.COUCH_DB_CHANGE_LOGS); this.dbHandlerUtil = new DatabaseHandlerUtil(dbChangeLogs); this.bulkDeleteUtil = new BulkDeleteUtil(this, componentRepository, releaseRepository, projectRepository, moderator, releaseModerator, attachmentConnector, attachmentDatabaseHandler, dbHandlerUtil); // Create the spdx document database handler - this.spdxDocumentDatabaseHandler = new SpdxDocumentDatabaseHandler(httpClient, DatabaseSettings.COUCH_DB_SPDX); + this.spdxDocumentDatabaseHandler = new SpdxDocumentDatabaseHandler(client, DatabaseSettings.COUCH_DB_SPDX); } - public ComponentDatabaseHandler(Supplier httpClient, String dbName, String changeLogsDbName, String attachmentDbName, ComponentModerator moderator, ReleaseModerator releaseModerator, ProjectModerator projectModerator) throws MalformedURLException { - this(httpClient, dbName, attachmentDbName, moderator, releaseModerator, projectModerator); - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(httpClient, changeLogsDbName); + public ComponentDatabaseHandler(Cloudant client, String dbName, String changeLogsDbName, String attachmentDbName, ComponentModerator moderator, ReleaseModerator releaseModerator, ProjectModerator projectModerator) throws MalformedURLException { + this(client, dbName, attachmentDbName, moderator, releaseModerator, projectModerator); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, changeLogsDbName); this.dbHandlerUtil = new DatabaseHandlerUtil(db); } - public ComponentDatabaseHandler(Supplier supplier, String dbName, String attachmentDbName) throws MalformedURLException { - this(supplier, dbName, attachmentDbName, new ComponentModerator(), new ReleaseModerator(), new ProjectModerator()); + public ComponentDatabaseHandler(Cloudant client, String dbName, String attachmentDbName) throws MalformedURLException { + this(client, dbName, attachmentDbName, new ComponentModerator(), new ReleaseModerator(), new ProjectModerator()); } - public ComponentDatabaseHandler(Supplier supplier, String dbName, String changelogsDbName, String attachmentDbName) throws MalformedURLException { - this(supplier, dbName, attachmentDbName, new ComponentModerator(), new ReleaseModerator(), new ProjectModerator()); - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(supplier, changelogsDbName); + public ComponentDatabaseHandler(Cloudant client, String dbName, String changelogsDbName, String attachmentDbName) throws MalformedURLException { + this(client, dbName, attachmentDbName, new ComponentModerator(), new ReleaseModerator(), new ProjectModerator()); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, changelogsDbName); this.dbHandlerUtil = new DatabaseHandlerUtil(db); } - public ComponentDatabaseHandler(Supplier httpClient, String dbName, String changeLogsDbName, String attachmentDbName, ThriftClients thriftClients) throws MalformedURLException { - this(httpClient, dbName, attachmentDbName, new ComponentModerator(thriftClients), new ReleaseModerator(thriftClients), new ProjectModerator(thriftClients)); + public ComponentDatabaseHandler(Cloudant client, String dbName, String changeLogsDbName, String attachmentDbName, ThriftClients thriftClients) throws MalformedURLException { + this(client, dbName, attachmentDbName, new ComponentModerator(thriftClients), new ReleaseModerator(thriftClients), new ProjectModerator(thriftClients)); } private void autosetReleaseClearingState(Release releaseAfter, Release releaseBefore) { @@ -1343,7 +1342,7 @@ public RequestSummary updateReleases(Collection releases, User user, bo RequestSummary requestSummary = new RequestSummary(); if (allowUpdate || PermissionUtils.isAdmin(user)) { // Prepare component for database - final List documentOperationResults = componentRepository.executeBulk(storedReleases); + final List documentOperationResults = componentRepository.executeBulk(storedReleases); if (!documentOperationResults.isEmpty()) { @@ -2709,9 +2708,9 @@ public RequestStatus updateReleasesWithSvmTrackingFeedback() { } } }); - List documentOperationResults = releaseRepository.executeBulk(releases); - documentOperationResults = documentOperationResults.stream().filter(res -> res.getError() != null || res.getStatusCode() != HttpStatus.SC_CREATED) - .collect(Collectors.toList()); + List documentOperationResults = releaseRepository.executeBulk(releases); + documentOperationResults = documentOperationResults.stream().filter(res -> res.getError() != null || !res.isOk()) + .toList(); if (documentOperationResults.isEmpty()) { log.info(String.format("SVMTF: updated %d releases", releases.size())); } else { diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentRepository.java index 3f494454e1..f526b3f8f4 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentRepository.java @@ -18,7 +18,7 @@ import org.eclipse.sw360.datahandler.thrift.components.Component; import org.eclipse.sw360.datahandler.thrift.users.User; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import com.cloudant.client.api.views.Key; import com.cloudant.client.api.views.UnpaginatedRequestBuilder; import com.cloudant.client.api.views.ViewRequest; @@ -161,7 +161,7 @@ public class ComponentRepository extends SummaryAwareRepository { public ComponentRepository(DatabaseConnectorCloudant db, ReleaseRepository releaseRepository, VendorRepository vendorRepository) { super(Component.class, db, new ComponentSummary(releaseRepository, vendorRepository)); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); views.put("byCreatedOn", createMapReduce(BY_CREATED_ON, null)); views.put("usedAttachmentContents", createMapReduce(USED_ATTACHMENT_CONTENTS, null)); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java index 48a1d1c2f3..391a400013 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java @@ -9,12 +9,11 @@ */ package org.eclipse.sw360.datahandler.db; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.gson.Gson; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.components.Component; import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; @@ -28,7 +27,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; import static org.eclipse.sw360.common.utils.SearchUtils.OBJ_ARRAY_TO_STRING_INDEX; import static org.eclipse.sw360.datahandler.permissions.PermissionUtils.makePermission; @@ -80,10 +78,10 @@ public class ComponentSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public ComponentSearchHandler(Supplier cClient, String dbName) throws IOException { + public ComponentSearchHandler(Cloudant cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); - Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); searchView.addNouveau(luceneSearchView, gson); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ConfigContainerRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ConfigContainerRepository.java index bc4da43db7..de402e5e8e 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ConfigContainerRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ConfigContainerRepository.java @@ -14,7 +14,7 @@ import org.eclipse.sw360.datahandler.thrift.ConfigContainer; import org.eclipse.sw360.datahandler.thrift.ConfigFor; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import com.cloudant.client.api.views.Key; import com.cloudant.client.api.views.UnpaginatedRequestBuilder; import com.cloudant.client.api.views.ViewRequestBuilder; @@ -30,7 +30,7 @@ public class ConfigContainerRepository extends DatabaseRepositoryCloudantClient< public ConfigContainerRepository(DatabaseConnectorCloudant databaseConnector) { super(databaseConnector, ConfigContainer.class); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); views.put("byId", createMapReduce(BYID, null)); views.put("byConfigFor", createMapReduce(BYCONFIGFOR, null)); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/CustomPropertiesRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/CustomPropertiesRepository.java index 71f78a8161..88bb6d5c98 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/CustomPropertiesRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/CustomPropertiesRepository.java @@ -16,7 +16,7 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.CustomProperties; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import java.util.HashMap; import java.util.List; @@ -41,7 +41,7 @@ public class CustomPropertiesRepository extends DatabaseRepositoryCloudantClient public CustomPropertiesRepository(DatabaseConnectorCloudant db) { super(db, CustomProperties.class); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("customPropertiesByDocType", createMapReduce(CUSTOM_PROPERTIES_BY_DOCTYPE, null)); views.put("all", createMapReduce(ALL, null)); initStandardDesignDocument(views, db); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java index e8b31fc119..5f33e398f8 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java @@ -9,10 +9,9 @@ */ package org.eclipse.sw360.datahandler.db; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.gson.Gson; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; @@ -23,7 +22,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; import static org.eclipse.sw360.common.utils.SearchUtils.OBJ_ARRAY_TO_STRING_INDEX; import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; @@ -65,10 +63,10 @@ public class ModerationSearchHandler { "}")); private final NouveauLuceneAwareDatabaseConnector connector; - public ModerationSearchHandler(Supplier cClient, String dbName) throws IOException { - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); - Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + public ModerationSearchHandler(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); searchView.addNouveau(luceneSearchView, gson); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java index 7aa9f4cb7d..f4f44c3830 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java @@ -10,7 +10,7 @@ */ package org.eclipse.sw360.datahandler.db; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.gson.Gson; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; @@ -23,7 +23,6 @@ import java.io.IOException; import java.util.List; -import java.util.function.Supplier; import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; @@ -51,11 +50,11 @@ public class ObligationElementSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public ObligationElementSearchHandler(Supplier cClient, String dbName) throws IOException { + public ObligationElementSearchHandler(Cloudant cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); // Creates the database connector and adds the lucene search view - connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); - Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); searchView.addNouveau(luceneSearchView, gson); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationListRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationListRepository.java index 91df3ee8d3..6a54832dd0 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationListRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationListRepository.java @@ -17,7 +17,7 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.projects.ObligationList; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; /** * CRUD access for the Project class @@ -37,7 +37,7 @@ public class ObligationListRepository extends DatabaseRepositoryCloudantClient views = new HashMap(); + Map views = new HashMap<>(); views.put("byProjectId", createMapReduce(BY_PROJECT_ID, null)); views.put("all", createMapReduce(ALL, null)); initStandardDesignDocument(views, db); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java index b1e2be684c..2cc6bda50e 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java @@ -10,10 +10,9 @@ */ package org.eclipse.sw360.datahandler.db; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.gson.Gson; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettings; import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.licenses.Obligation; @@ -23,7 +22,6 @@ import java.io.IOException; import java.util.List; -import java.util.function.Supplier; import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; @@ -47,11 +45,11 @@ public class ObligationSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public ObligationSearchHandler(Supplier cClient, String dbName) throws IOException { + public ObligationSearchHandler(Cloudant cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); // Creates the database connector and adds the lucene search view - connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); - Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); searchView.addNouveau(luceneSearchView, gson); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageDatabaseHandler.java index 2cf4cab8bd..d15e544894 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageDatabaseHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageDatabaseHandler.java @@ -21,10 +21,8 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; -import java.util.function.Supplier; import java.util.stream.Collectors; -import org.apache.http.HttpStatus; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.eclipse.sw360.cyclonedx.CycloneDxBOMImporter; @@ -49,8 +47,8 @@ import org.eclipse.sw360.datahandler.thrift.packages.PackageManager; import org.eclipse.sw360.datahandler.thrift.users.User; -import com.cloudant.client.api.CloudantClient; -import com.cloudant.client.api.model.Response; +import com.ibm.cloud.cloudant.v1.Cloudant; +import com.ibm.cloud.cloudant.v1.model.DocumentResult; import com.github.packageurl.MalformedPackageURLException; import com.github.packageurl.PackageURL; import com.google.common.collect.ImmutableList; @@ -77,29 +75,29 @@ public class PackageDatabaseHandler extends AttachmentAwareDatabaseHandler { Package._Fields.NAME, Package._Fields.VERSION, Package._Fields.VCS, Package._Fields.DESCRIPTION, Package._Fields.HOMEPAGE_URL, Package._Fields.PURL, Package._Fields.HASH); - public PackageDatabaseHandler(Supplier httpClient, String dbName, String attachmentDbName, String changeLogsDbName, + public PackageDatabaseHandler(Cloudant client, String dbName, String attachmentDbName, String changeLogsDbName, AttachmentDatabaseHandler attachmentDatabaseHandler, ComponentDatabaseHandler componentDatabaseHandler) throws MalformedURLException { super(attachmentDatabaseHandler); - db = new DatabaseConnectorCloudant(httpClient, dbName); + db = new DatabaseConnectorCloudant(client, dbName); // Create the repositories packageRepository = new PackageRepository(db); projectRepository = new ProjectRepository(db); // Create the attachment connector - attachmentConnector = new AttachmentConnector(httpClient, attachmentDbName, Duration.durationOf(30, TimeUnit.SECONDS)); + attachmentConnector = new AttachmentConnector(client, attachmentDbName, Duration.durationOf(30, TimeUnit.SECONDS)); this.componentDatabaseHandler = componentDatabaseHandler; - DatabaseConnectorCloudant changeLogsDb = new DatabaseConnectorCloudant(httpClient, changeLogsDbName); + DatabaseConnectorCloudant changeLogsDb = new DatabaseConnectorCloudant(client, changeLogsDbName); this.databaseHandlerUtil = new DatabaseHandlerUtil(changeLogsDb); } - public PackageDatabaseHandler(Supplier httpClient, String dbName, String changeLogsDbName, String attachmentDbName) + public PackageDatabaseHandler(Cloudant client, String dbName, String changeLogsDbName, String attachmentDbName) throws MalformedURLException { - this(httpClient, dbName, attachmentDbName, changeLogsDbName, new AttachmentDatabaseHandler(httpClient, dbName, attachmentDbName), - new ComponentDatabaseHandler(httpClient, dbName, changeLogsDbName, attachmentDbName)); + this(client, dbName, attachmentDbName, changeLogsDbName, new AttachmentDatabaseHandler(client, dbName, attachmentDbName), + new ComponentDatabaseHandler(client, dbName, changeLogsDbName, attachmentDbName)); } public Package getPackageById(String id) throws SW360Exception { @@ -206,7 +204,7 @@ public AddDocumentRequestSummary addPackage(Package pkg, User user) throws SW360 preparePackage(pkg); List duplicatePackagesByPurl = getPackageByPurl(pkg.getPurl()); - if (duplicatePackagesByPurl.size() > 0) { + if (!duplicatePackagesByPurl.isEmpty()) { final AddDocumentRequestSummary addDocumentRequestSummary = new AddDocumentRequestSummary() .setRequestStatus(AddDocumentRequestStatus.DUPLICATE) .setMessage(SW360Constants.DUPLICATE_PACKAGE_BY_PURL); @@ -216,7 +214,7 @@ public AddDocumentRequestSummary addPackage(Package pkg, User user) throws SW360 return addDocumentRequestSummary; }else{ List duplicatePackages = getPackageByNameAndVersion(name, version); - if(duplicatePackages.size() > 0){ + if(!duplicatePackages.isEmpty()){ final AddDocumentRequestSummary addDocumentRequestSummary = new AddDocumentRequestSummary() .setRequestStatus(AddDocumentRequestStatus.DUPLICATE); if(duplicatePackages.size() == 1) { @@ -317,12 +315,12 @@ public RequestStatus updatePackage(Package updatedPkg, User user) throws SW360Ex // Ensure that release exists try { // once we are sure that release exists, update the package - Response resp = packageRepository.updateWithResponse(updatedPkg); + DocumentResult resp = packageRepository.updateWithResponse(updatedPkg); if (CommonUtils.isNotNullEmptyOrWhitespace(actualReleaseId)) { Release actualRelease = componentDatabaseHandler.getRelease(actualReleaseId, user); Set packageIds = CommonUtils.nullToEmptySet(actualRelease.getPackageIds()); - // Update the previously linked release, if it contain package id - if (resp.getStatusCode() == HttpStatus.SC_CREATED) { + // Update the previously linked release, if it contains package id + if (resp.isOk()) { if (packageIds.contains(packageId)) { packageIds.remove(packageId); actualRelease.setPackageIds(packageIds); @@ -339,7 +337,7 @@ public RequestStatus updatePackage(Package updatedPkg, User user) throws SW360Ex Release newRelease = componentDatabaseHandler.getRelease(newReleaseId, user); Set packageIds = CommonUtils.nullToEmptySet(newRelease.getPackageIds()); // Update the newly linked release, if it does not contain package id - if (resp.getStatusCode() == HttpStatus.SC_CREATED) { + if (resp.isOk()) { if (!packageIds.contains(packageId)) { newRelease.addToPackageIds(packageId); componentDatabaseHandler.updateRelease(newRelease, user, ThriftUtils.IMMUTABLE_OF_RELEASE, true); @@ -411,7 +409,7 @@ private boolean changeWouldResultInDuplicate(Package before, Package after) { return false; } List duplicates = getPackageByNameAndVersion(after.getName(), after.getVersion()); - return duplicates.size() > 0; + return !duplicates.isEmpty(); } public List searchByName(String name) { diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageRepository.java index d6fac977ea..6c2e0fbc78 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageRepository.java @@ -23,7 +23,7 @@ import org.eclipse.sw360.datahandler.thrift.packages.Package; import org.eclipse.sw360.datahandler.thrift.users.User; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import com.cloudant.client.api.views.Key; import com.cloudant.client.api.views.ViewRequest; import com.cloudant.client.api.views.ViewRequestBuilder; @@ -54,7 +54,7 @@ public class PackageRepository extends DatabaseRepositoryCloudantClient public PackageRepository(DatabaseConnectorCloudant db) { super(db, Package.class); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); views.put("orphan", createMapReduce(ORPHAN, null)); views.put("byName", createMapReduce(BY_NAME, null)); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java index e7b668f94b..a35e66d899 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java @@ -9,10 +9,9 @@ */ package org.eclipse.sw360.datahandler.db; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.gson.Gson; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.packages.Package; import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; @@ -23,7 +22,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; import static org.eclipse.sw360.common.utils.SearchUtils.OBJ_ARRAY_TO_STRING_INDEX; import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; @@ -74,10 +72,10 @@ public class PackageSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public PackageSearchHandler(Supplier cClient, String dbName) throws IOException { - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); - Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + public PackageSearchHandler(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); searchView.addNouveau(luceneSearchView, gson); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java index 028e3efb4f..65fb4dc108 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java @@ -13,7 +13,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.collect.*; @@ -72,7 +72,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -151,35 +150,35 @@ public class ProjectDatabaseHandler extends AttachmentAwareDatabaseHandler { private Map cachedAllProjectsIdMap; private Instant cachedAllProjectsIdMapLoadingInstant; - public ProjectDatabaseHandler(Supplier httpClient, String dbName, String attachmentDbName) throws MalformedURLException { - this(httpClient, dbName, attachmentDbName, new ProjectModerator(), - new ComponentDatabaseHandler(httpClient,dbName,attachmentDbName), - new PackageDatabaseHandler(httpClient, dbName, DatabaseSettings.COUCH_DB_CHANGE_LOGS, attachmentDbName), - new AttachmentDatabaseHandler(httpClient, dbName, attachmentDbName)); + public ProjectDatabaseHandler(Cloudant client, String dbName, String attachmentDbName) throws MalformedURLException { + this(client, dbName, attachmentDbName, new ProjectModerator(), + new ComponentDatabaseHandler(client,dbName,attachmentDbName), + new PackageDatabaseHandler(client, dbName, DatabaseSettings.COUCH_DB_CHANGE_LOGS, attachmentDbName), + new AttachmentDatabaseHandler(client, dbName, attachmentDbName)); } - public ProjectDatabaseHandler(Supplier httpClient, String dbName, String changeLogDbName, String attachmentDbName) throws MalformedURLException { - this(httpClient, dbName, changeLogDbName, attachmentDbName, new ProjectModerator(), - new ComponentDatabaseHandler(httpClient, dbName, attachmentDbName), - new PackageDatabaseHandler(httpClient, dbName, changeLogDbName, attachmentDbName), - new AttachmentDatabaseHandler(httpClient, dbName, attachmentDbName)); + public ProjectDatabaseHandler(Cloudant client, String dbName, String changeLogDbName, String attachmentDbName) throws MalformedURLException { + this(client, dbName, changeLogDbName, attachmentDbName, new ProjectModerator(), + new ComponentDatabaseHandler(client, dbName, attachmentDbName), + new PackageDatabaseHandler(client, dbName, changeLogDbName, attachmentDbName), + new AttachmentDatabaseHandler(client, dbName, attachmentDbName)); } @VisibleForTesting - public ProjectDatabaseHandler(Supplier httpClient, String dbName, String changeLogsDbName, String attachmentDbName, ProjectModerator moderator, + public ProjectDatabaseHandler(Cloudant client, String dbName, String changeLogsDbName, String attachmentDbName, ProjectModerator moderator, ComponentDatabaseHandler componentDatabaseHandler, PackageDatabaseHandler packageDatabaseHandler, AttachmentDatabaseHandler attachmentDatabaseHandler) throws MalformedURLException { - this(httpClient, dbName, attachmentDbName, moderator, componentDatabaseHandler, packageDatabaseHandler, attachmentDatabaseHandler); - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(httpClient, changeLogsDbName); + this(client, dbName, attachmentDbName, moderator, componentDatabaseHandler, packageDatabaseHandler, attachmentDatabaseHandler); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, changeLogsDbName); this.dbHandlerUtil = new DatabaseHandlerUtil(db); } @VisibleForTesting - public ProjectDatabaseHandler(Supplier httpClient, String dbName, String attachmentDbName, ProjectModerator moderator, + public ProjectDatabaseHandler(Cloudant client, String dbName, String attachmentDbName, ProjectModerator moderator, ComponentDatabaseHandler componentDatabaseHandler, PackageDatabaseHandler packageDatabaseHandler, AttachmentDatabaseHandler attachmentDatabaseHandler) throws MalformedURLException { super(attachmentDatabaseHandler); - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(httpClient, dbName); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); // Create the repositories repository = new ProjectRepository(db); @@ -194,11 +193,11 @@ public ProjectDatabaseHandler(Supplier httpClient, String dbName this.moderator = moderator; // Create the attachment connector - attachmentConnector = new AttachmentConnector(httpClient, attachmentDbName, Duration.durationOf(30, TimeUnit.SECONDS)); + attachmentConnector = new AttachmentConnector(client, attachmentDbName, Duration.durationOf(30, TimeUnit.SECONDS)); this.componentDatabaseHandler = componentDatabaseHandler; this.packageDatabaseHandler = packageDatabaseHandler; - DatabaseConnectorCloudant dbChangelogs = new DatabaseConnectorCloudant(httpClient, DatabaseSettings.COUCH_DB_CHANGE_LOGS); + DatabaseConnectorCloudant dbChangelogs = new DatabaseConnectorCloudant(client, DatabaseSettings.COUCH_DB_CHANGE_LOGS); this.dbHandlerUtil = new DatabaseHandlerUtil(dbChangelogs); } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectRepository.java index c109dd393a..ac467c55f7 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectRepository.java @@ -28,7 +28,7 @@ import org.eclipse.sw360.datahandler.thrift.users.UserGroup; import org.jetbrains.annotations.NotNull; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import com.cloudant.client.api.query.PredicateExpression; import com.cloudant.client.api.query.PredicatedOperation; import com.cloudant.client.api.query.QueryBuilder; @@ -266,7 +266,7 @@ public class ProjectRepository extends SummaryAwareRepository { public ProjectRepository(DatabaseConnectorCloudant db) { super(Project.class, db, new ProjectSummary()); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("byname", createMapReduce(BY_NAME_VIEW, null)); views.put("bygroup", createMapReduce(BY_GROUP_VIEW, null)); views.put("bytag", createMapReduce(BY_TAG_VIEW, null)); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java index 99a93920b3..f7168ade9e 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java @@ -9,10 +9,9 @@ */ package org.eclipse.sw360.datahandler.db; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.gson.Gson; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettings; import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.projects.Project; @@ -28,7 +27,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; import static org.eclipse.sw360.common.utils.SearchUtils.OBJ_ARRAY_TO_STRING_INDEX; @@ -82,10 +80,10 @@ public class ProjectSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public ProjectSearchHandler(Supplier cClient, String dbName) throws IOException { - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); - Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + public ProjectSearchHandler(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); searchView.addNouveau(luceneSearchView, gson); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectVulnerabilityRatingRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectVulnerabilityRatingRepository.java index b9915a7c3f..3029b2cbf1 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectVulnerabilityRatingRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectVulnerabilityRatingRepository.java @@ -17,7 +17,7 @@ import org.eclipse.sw360.datahandler.thrift.vulnerabilities.ProjectVulnerabilityRating; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import java.util.HashMap; import java.util.List; @@ -58,7 +58,7 @@ public class ProjectVulnerabilityRatingRepository extends DatabaseRepositoryClou public ProjectVulnerabilityRatingRepository(DatabaseConnectorCloudant db) { super(db, ProjectVulnerabilityRating.class); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("projectVulnerabilityRating", createMapReduce(PROJECT_VULNERABILITY_LINK, null)); views.put("all", createMapReduce(ALL, null)); views.put("byReleaseId", createMapReduce(BYRELEASEID, null)); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/RelationsUsageRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/RelationsUsageRepository.java index 65e4b9bf1c..f4a365cd97 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/RelationsUsageRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/RelationsUsageRepository.java @@ -17,7 +17,7 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.projects.UsedReleaseRelations; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import com.cloudant.client.api.views.Key; import com.cloudant.client.api.views.UnpaginatedRequestBuilder; import com.cloudant.client.api.views.ViewRequestBuilder; @@ -42,7 +42,7 @@ public class RelationsUsageRepository extends DatabaseRepositoryCloudantClient views = new HashMap(); + Map views = new HashMap<>(); views.put("byProjectId", createMapReduce(BY_PROJECT_ID, null)); views.put("all", createMapReduce(ALL, null)); initStandardDesignDocument(views, db); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseRepository.java index aee6803f4b..0a34660e0e 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseRepository.java @@ -19,7 +19,7 @@ import org.eclipse.sw360.datahandler.thrift.PaginationData; import java.util.*; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import com.cloudant.client.api.views.Key; import com.cloudant.client.api.views.UnpaginatedRequestBuilder; import com.cloudant.client.api.views.ViewRequestBuilder; @@ -169,7 +169,7 @@ public class ReleaseRepository extends SummaryAwareRepository { public ReleaseRepository(DatabaseConnectorCloudant db, VendorRepository vendorRepository) { super(Release.class, db, new ReleaseSummary(vendorRepository)); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); views.put("byname", createMapReduce(BY_NAME, null)); views.put("byCreatedOn", createMapReduce(BY_CREATED_ON, null)); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java index f5484e9a1e..f9dece6bb8 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java @@ -9,10 +9,9 @@ */ package org.eclipse.sw360.datahandler.db; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.gson.Gson; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.components.Release; import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; @@ -21,7 +20,6 @@ import java.io.IOException; import java.util.List; -import java.util.function.Supplier; import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; @@ -52,10 +50,10 @@ public class ReleaseSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public ReleaseSearchHandler(Supplier cClient, String dbName) throws IOException { + public ReleaseSearchHandler(Cloudant cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); - Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); searchView.addNouveau(luceneSearchView, gson); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserRepository.java index aaf83b4f67..d2f9e19610 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserRepository.java @@ -20,7 +20,7 @@ import org.eclipse.sw360.datahandler.thrift.PaginationData; import org.eclipse.sw360.datahandler.thrift.users.User; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import com.cloudant.client.api.query.Expression; import com.cloudant.client.api.query.QueryBuilder; import com.cloudant.client.api.query.QueryResult; @@ -120,7 +120,7 @@ public class UserRepository extends SummaryAwareRepository { public UserRepository(DatabaseConnectorCloudant databaseConnector) { super(User.class, databaseConnector, new UserSummary()); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); views.put("byExternalId", createMapReduce(BYEXTERNALID, null)); views.put("byApiToken", createMapReduce(BYAPITOKEN, null)); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java index 9817c44a16..16214dd9b1 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java @@ -9,10 +9,9 @@ */ package org.eclipse.sw360.datahandler.db; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.gson.Gson; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; @@ -23,7 +22,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareFuzzyQuery; import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; @@ -71,11 +69,11 @@ public class UserSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public UserSearchHandler(Supplier cClient, String dbName) throws IOException { - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); + public UserSearchHandler(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); // Creates the database connector and adds the lucene search view - connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); - Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); searchView.addNouveau(luceneSearchView, gson); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorRepository.java index c7902c34b1..8a399fb26c 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorRepository.java @@ -25,7 +25,7 @@ import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; import java.util.Set; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; /** * CRUD access for the Vendor class @@ -51,7 +51,7 @@ public class VendorRepository extends DatabaseRepositoryCloudantClient { public VendorRepository(DatabaseConnectorCloudant db) { super(db, Vendor.class); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); views.put("vendorbyshortname", createMapReduce(BY_LOWERCASE_VENDOR_SHORTNAME_VIEW, null)); views.put("vendorbyfullname", createMapReduce(BY_LOWERCASE_VENDOR_FULLNAME_VIEW, null)); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java index 8fb07f05b4..000d863725 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java @@ -9,11 +9,9 @@ */ package org.eclipse.sw360.datahandler.db; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.gson.Gson; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; @@ -22,7 +20,6 @@ import java.io.IOException; import java.util.List; -import java.util.function.Supplier; import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; @@ -54,11 +51,11 @@ public class VendorSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public VendorSearchHandler(DatabaseConnector databaseConnector, Supplier cClient) throws IOException { + public VendorSearchHandler(Cloudant client, String dbName) throws IOException { // Creates the database connector and adds the lucene search view - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, databaseConnector.getDbName()); - connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); - Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); searchView.addNouveau(luceneSearchView, gson); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentDatabaseHandler.java index 57885c8ad2..a622d03e48 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentDatabaseHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentDatabaseHandler.java @@ -10,7 +10,7 @@ */ package org.eclipse.sw360.datahandler.db.spdx.document; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettings; @@ -33,7 +33,6 @@ import java.net.MalformedURLException; import java.util.*; -import java.util.function.Supplier; import com.google.common.collect.Lists; import static org.eclipse.sw360.datahandler.common.SW360Assert.assertNotNull; @@ -61,22 +60,22 @@ public class SpdxDocumentDatabaseHandler { private final SpdxDocumentCreationInfoDatabaseHandler creationInfoDatabaseHandler; private final SpdxPackageInfoDatabaseHandler packageInfoDatabaseHandler; - public SpdxDocumentDatabaseHandler(Supplier httpClient, String dbName) throws MalformedURLException { - db = new DatabaseConnectorCloudant(httpClient, dbName); + public SpdxDocumentDatabaseHandler(Cloudant client, String dbName) throws MalformedURLException { + db = new DatabaseConnectorCloudant(client, dbName); // Create the repositories SPDXDocumentRepository = new SpdxDocumentRepository(db); - sw360db = new DatabaseConnectorCloudant(httpClient, DatabaseSettings.COUCH_DB_DATABASE); + sw360db = new DatabaseConnectorCloudant(client, DatabaseSettings.COUCH_DB_DATABASE); vendorRepository = new VendorRepository(sw360db); releaseRepository = new ReleaseRepository(sw360db, vendorRepository); // Create the moderator moderator = new SpdxDocumentModerator(); // Create the changelogs - dbChangeLogs = new DatabaseConnectorCloudant(httpClient, DatabaseSettings.COUCH_DB_CHANGE_LOGS); + dbChangeLogs = new DatabaseConnectorCloudant(client, DatabaseSettings.COUCH_DB_CHANGE_LOGS); this.dbHandlerUtil = new DatabaseHandlerUtil(dbChangeLogs); - this.creationInfoDatabaseHandler = new SpdxDocumentCreationInfoDatabaseHandler(httpClient, dbName); - this.packageInfoDatabaseHandler = new SpdxPackageInfoDatabaseHandler(httpClient, dbName); + this.creationInfoDatabaseHandler = new SpdxDocumentCreationInfoDatabaseHandler(client, dbName); + this.packageInfoDatabaseHandler = new SpdxPackageInfoDatabaseHandler(client, dbName); } public List getSPDXDocumentSummary(User user) { diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentRepository.java index 25f024b187..6d9dac9db9 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentRepository.java @@ -16,7 +16,7 @@ import org.eclipse.sw360.datahandler.couchdb.SummaryAwareRepository; import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import java.util.HashMap; import java.util.List; @@ -28,7 +28,7 @@ public class SpdxDocumentRepository extends SummaryAwareRepository public SpdxDocumentRepository(DatabaseConnectorCloudant db) { super(SPDXDocument.class, db, new SpdxDocumentSummary()); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); initStandardDesignDocument(views, db); } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java index db807b0bb9..63231426bc 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java @@ -10,10 +10,9 @@ */ package org.eclipse.sw360.datahandler.db.spdx.document; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.gson.Gson; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; @@ -22,7 +21,6 @@ import java.io.IOException; import java.util.List; -import java.util.function.Supplier; import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; @@ -42,10 +40,10 @@ public class SpdxDocumentSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public SpdxDocumentSearchHandler(Supplier cClient, String dbName) throws IOException { - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); - Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + public SpdxDocumentSearchHandler(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); searchView.addNouveau(luceneSearchView, gson); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoDatabaseHandler.java index 9da2667d05..b63f952809 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoDatabaseHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoDatabaseHandler.java @@ -10,7 +10,7 @@ */ package org.eclipse.sw360.datahandler.db.spdx.documentcreationinfo; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.db.spdx.document.SpdxDocumentRepository; @@ -29,7 +29,6 @@ import java.net.MalformedURLException; import java.util.*; -import java.util.function.Supplier; import com.google.common.collect.Lists; import org.eclipse.sw360.datahandler.common.DatabaseSettings; @@ -53,8 +52,8 @@ public class SpdxDocumentCreationInfoDatabaseHandler { private DatabaseHandlerUtil dbHandlerUtil; private final SpdxDocumentCreationInfoModerator moderator; - public SpdxDocumentCreationInfoDatabaseHandler(Supplier httpClient, String dbName) throws MalformedURLException { - db = new DatabaseConnectorCloudant(httpClient, dbName); + public SpdxDocumentCreationInfoDatabaseHandler(Cloudant client, String dbName) throws MalformedURLException { + db = new DatabaseConnectorCloudant(client, dbName); // Create the repositories SPDXDocumentCreationInfoRepository = new SpdxDocumentCreationInfoRepository(db); @@ -62,7 +61,7 @@ public SpdxDocumentCreationInfoDatabaseHandler(Supplier httpClie // Create the moderator moderator = new SpdxDocumentCreationInfoModerator(); // Create the changelogs - dbChangeLogs = new DatabaseConnectorCloudant(httpClient, DatabaseSettings.COUCH_DB_CHANGE_LOGS); + dbChangeLogs = new DatabaseConnectorCloudant(client, DatabaseSettings.COUCH_DB_CHANGE_LOGS); this.dbHandlerUtil = new DatabaseHandlerUtil(dbChangeLogs); } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoRepository.java index 8978eda05f..925985db27 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoRepository.java @@ -16,7 +16,7 @@ import org.eclipse.sw360.datahandler.couchdb.SummaryAwareRepository; import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.*; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import java.util.HashMap; import java.util.List; @@ -28,7 +28,7 @@ public class SpdxDocumentCreationInfoRepository extends SummaryAwareRepository views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); initStandardDesignDocument(views, db); } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java index b35e57aee8..fb8d18eaf8 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java @@ -10,10 +10,9 @@ */ package org.eclipse.sw360.datahandler.db.spdx.documentcreationinfo; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.gson.Gson; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; @@ -22,7 +21,6 @@ import java.io.IOException; import java.util.List; -import java.util.function.Supplier; import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; @@ -42,10 +40,10 @@ public class SpdxDocumentCreationInfoSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public SpdxDocumentCreationInfoSearchHandler(Supplier cClient, String dbName) throws IOException { - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); - Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + public SpdxDocumentCreationInfoSearchHandler(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); searchView.addNouveau(luceneSearchView, gson); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoDatabaseHandler.java index 4ee66bea41..5c2cb77958 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoDatabaseHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoDatabaseHandler.java @@ -10,7 +10,7 @@ */ package org.eclipse.sw360.datahandler.db.spdx.packageinfo; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.db.spdx.document.SpdxDocumentRepository; @@ -29,7 +29,6 @@ import java.net.MalformedURLException; import java.util.*; -import java.util.function.Supplier; import com.google.common.collect.Lists; import org.eclipse.sw360.datahandler.common.DatabaseSettings; @@ -53,8 +52,8 @@ public class SpdxPackageInfoDatabaseHandler { private DatabaseHandlerUtil dbHandlerUtil; private final SpdxPackageInfoModerator moderator; - public SpdxPackageInfoDatabaseHandler(Supplier httpClient, String dbName) throws MalformedURLException { - db = new DatabaseConnectorCloudant(httpClient, dbName); + public SpdxPackageInfoDatabaseHandler(Cloudant client, String dbName) throws MalformedURLException { + db = new DatabaseConnectorCloudant(client, dbName); // Create the repositories PackageInfoRepository = new SpdxPackageInfoRepository(db); @@ -63,13 +62,12 @@ public SpdxPackageInfoDatabaseHandler(Supplier httpClient, Strin // Create the moderator moderator = new SpdxPackageInfoModerator(); // Create the changelogs - dbChangeLogs = new DatabaseConnectorCloudant(httpClient, DatabaseSettings.COUCH_DB_CHANGE_LOGS); + dbChangeLogs = new DatabaseConnectorCloudant(client, DatabaseSettings.COUCH_DB_CHANGE_LOGS); this.dbHandlerUtil = new DatabaseHandlerUtil(dbChangeLogs); } public List getPackageInformationSummary(User user) { - List packageInfos = PackageInfoRepository.getPackageInformationSummary(); - return packageInfos; + return PackageInfoRepository.getPackageInformationSummary(); } public PackageInformation getPackageInformationById(String id, User user) throws SW360Exception { diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoRepository.java index 06554cd9c1..c4c526200b 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoRepository.java @@ -16,7 +16,7 @@ import org.eclipse.sw360.datahandler.couchdb.SummaryAwareRepository; import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.*; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import java.util.HashMap; import java.util.List; @@ -28,7 +28,7 @@ public class SpdxPackageInfoRepository extends SummaryAwareRepository views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); initStandardDesignDocument(views, db); } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java index 34675e4346..3f0647e2f1 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java @@ -10,10 +10,9 @@ */ package org.eclipse.sw360.datahandler.db.spdx.packageinfo; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.gson.Gson; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; @@ -22,7 +21,6 @@ import java.io.IOException; import java.util.List; -import java.util.function.Supplier; import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; @@ -42,10 +40,10 @@ public class SpdxPackageInfoSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; - public SpdxPackageInfoSearchHandler(Supplier cClient, String dbName) throws IOException { - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); - Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + public SpdxPackageInfoSearchHandler(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); searchView.addNouveau(luceneSearchView, gson); diff --git a/backend/src/src-attachments/src/main/java/org/eclipse/sw360/attachments/AttachmentHandler.java b/backend/src/src-attachments/src/main/java/org/eclipse/sw360/attachments/AttachmentHandler.java index 749c6523d0..d5b90e03ad 100644 --- a/backend/src/src-attachments/src/main/java/org/eclipse/sw360/attachments/AttachmentHandler.java +++ b/backend/src/src-attachments/src/main/java/org/eclipse/sw360/attachments/AttachmentHandler.java @@ -9,8 +9,8 @@ */ package org.eclipse.sw360.attachments; -import com.cloudant.client.api.CloudantClient; import com.google.common.collect.ImmutableSet; +import com.ibm.cloud.cloudant.v1.Cloudant; import org.apache.thrift.TException; import org.eclipse.sw360.datahandler.db.AttachmentDatabaseHandler; import org.eclipse.sw360.datahandler.common.DatabaseSettings; @@ -26,7 +26,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; import static org.eclipse.sw360.datahandler.common.SW360Assert.*; @@ -46,8 +45,8 @@ public AttachmentHandler() throws MalformedURLException { handler = new AttachmentDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE, DatabaseSettings.COUCH_DB_ATTACHMENTS); } - public AttachmentHandler(Supplier httpClient, String dbName, String attachmentDbName) throws MalformedURLException { - handler = new AttachmentDatabaseHandler(httpClient, dbName, attachmentDbName); + public AttachmentHandler(Cloudant client, String dbName, String attachmentDbName) throws MalformedURLException { + handler = new AttachmentDatabaseHandler(client, dbName, attachmentDbName); } @Override diff --git a/backend/src/src-changelogs/src/main/java/org/eclipse/sw360/changelogs/ChangeLogsHandler.java b/backend/src/src-changelogs/src/main/java/org/eclipse/sw360/changelogs/ChangeLogsHandler.java index 89d4485ba1..f256f4966b 100644 --- a/backend/src/src-changelogs/src/main/java/org/eclipse/sw360/changelogs/ChangeLogsHandler.java +++ b/backend/src/src-changelogs/src/main/java/org/eclipse/sw360/changelogs/ChangeLogsHandler.java @@ -15,9 +15,7 @@ import java.io.IOException; import java.util.List; -import java.util.function.Supplier; -import org.apache.thrift.TException; import org.eclipse.sw360.datahandler.common.DatabaseSettings; import org.eclipse.sw360.datahandler.db.ChangeLogsDatabaseHandler; import org.eclipse.sw360.datahandler.thrift.SW360Exception; @@ -26,7 +24,7 @@ import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.RequestStatus; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; /** * Implementation of the Thrift service @@ -41,7 +39,7 @@ public class ChangeLogsHandler implements ChangeLogsService.Iface { this(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_CHANGE_LOGS); } - ChangeLogsHandler(Supplier client, String dbName) throws IOException { + ChangeLogsHandler(Cloudant client, String dbName) throws IOException { handler = new ChangeLogsDatabaseHandler(client, dbName); } diff --git a/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java b/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java index 5bd508d4ed..87fb1ab4f9 100644 --- a/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java +++ b/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java @@ -25,14 +25,13 @@ import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; import static org.eclipse.sw360.datahandler.common.SW360Assert.*; @@ -52,7 +51,7 @@ public ComponentHandler() throws IOException { this(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE, DatabaseSettings.COUCH_DB_CHANGE_LOGS, DatabaseSettings.COUCH_DB_ATTACHMENTS); } - public ComponentHandler(Supplier cClient, String dbName, String changeLogsDBName, String attachmentDbName) throws IOException { + public ComponentHandler(Cloudant cClient, String dbName, String changeLogsDBName, String attachmentDbName) throws IOException { handler = new ComponentDatabaseHandler(cClient, dbName, changeLogsDBName, attachmentDbName); componentSearchHandler = new ComponentSearchHandler(cClient, dbName); releaseSearchHandler = new ReleaseSearchHandler(cClient, dbName); @@ -63,7 +62,7 @@ public ComponentHandler(ThriftClients thriftClients) throws IOException { this(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE, DatabaseSettings.COUCH_DB_CHANGE_LOGS, DatabaseSettings.COUCH_DB_ATTACHMENTS, thriftClients); } - public ComponentHandler(Supplier client, String dbName, String changeLogsDBName, String attachmentDbName, ThriftClients thriftClients) throws IOException { + public ComponentHandler(Cloudant client, String dbName, String changeLogsDBName, String attachmentDbName, ThriftClients thriftClients) throws IOException { handler = new ComponentDatabaseHandler(client, dbName, changeLogsDBName, attachmentDbName, thriftClients); componentSearchHandler = new ComponentSearchHandler(client, dbName); releaseSearchHandler = new ReleaseSearchHandler(client, dbName); diff --git a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/BulkDeleteUtilTest.java b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/BulkDeleteUtilTest.java index e91ac2723a..b7c01a76de 100644 --- a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/BulkDeleteUtilTest.java +++ b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/BulkDeleteUtilTest.java @@ -14,7 +14,6 @@ import org.eclipse.sw360.datahandler.TestUtils; import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.db.ComponentDatabaseHandler; import org.eclipse.sw360.datahandler.db.BulkDeleteUtil; import org.eclipse.sw360.datahandler.entitlement.ComponentModerator; diff --git a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentDatabaseHandlerTest.java b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentDatabaseHandlerTest.java index b0760eeea3..9b265207de 100644 --- a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentDatabaseHandlerTest.java +++ b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentDatabaseHandlerTest.java @@ -17,7 +17,6 @@ import org.eclipse.sw360.datahandler.TestUtils; import org.eclipse.sw360.datahandler.common.SW360Constants; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.db.ComponentDatabaseHandler; import org.eclipse.sw360.datahandler.db.SvmConnector; import org.eclipse.sw360.datahandler.entitlement.ComponentModerator; diff --git a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentSearchHandlerTest.java b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentSearchHandlerTest.java index 2ca0d420a9..f395a521f6 100644 --- a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentSearchHandlerTest.java +++ b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentSearchHandlerTest.java @@ -13,8 +13,6 @@ import com.google.common.collect.ImmutableSet; import org.eclipse.sw360.datahandler.TestUtils; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.DatabaseInstance; import org.eclipse.sw360.datahandler.db.ComponentSearchHandler; import org.eclipse.sw360.datahandler.thrift.ThriftClients; import org.eclipse.sw360.datahandler.thrift.components.Component; diff --git a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ProjectDatabaseHandlerTest.java b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ProjectDatabaseHandlerTest.java index 5180369493..a40ad8d5cb 100644 --- a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ProjectDatabaseHandlerTest.java +++ b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ProjectDatabaseHandlerTest.java @@ -14,8 +14,6 @@ import org.eclipse.sw360.datahandler.TestUtils; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.common.DatabaseSettings; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.db.AttachmentDatabaseHandler; import org.eclipse.sw360.datahandler.db.ComponentDatabaseHandler; import org.eclipse.sw360.datahandler.db.PackageDatabaseHandler; @@ -31,7 +29,6 @@ import org.eclipse.sw360.datahandler.thrift.components.ReleaseLink; import org.eclipse.sw360.datahandler.thrift.projects.*; import org.eclipse.sw360.datahandler.thrift.users.User; -import org.eclipse.sw360.datahandler.thrift.users.UserGroup; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; import org.junit.After; import org.junit.Before; @@ -40,10 +37,8 @@ import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; -import javax.xml.crypto.Data; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -54,7 +49,6 @@ import static org.hamcrest.Matchers.contains; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class ProjectDatabaseHandlerTest { diff --git a/backend/src/src-cvesearch/src/main/java/org/eclipse/sw360/cvesearch/datasink/VulnerabilityConnector.java b/backend/src/src-cvesearch/src/main/java/org/eclipse/sw360/cvesearch/datasink/VulnerabilityConnector.java index 39563c222d..0b7b7f1290 100644 --- a/backend/src/src-cvesearch/src/main/java/org/eclipse/sw360/cvesearch/datasink/VulnerabilityConnector.java +++ b/backend/src/src-cvesearch/src/main/java/org/eclipse/sw360/cvesearch/datasink/VulnerabilityConnector.java @@ -10,7 +10,7 @@ */ package org.eclipse.sw360.cvesearch.datasink; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.common.base.Strings; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; @@ -26,11 +26,9 @@ import org.eclipse.sw360.datahandler.thrift.vulnerabilities.Vulnerability; import org.eclipse.sw360.vulnerabilities.common.VulnerabilityMapper; import org.eclipse.sw360.vulnerabilities.db.VulnerabilityDatabaseHandler; -import org.ektorp.http.HttpClient; import java.io.IOException; import java.util.*; -import java.util.function.Supplier; public class VulnerabilityConnector { @@ -52,11 +50,11 @@ public VulnerabilityConnector() throws IOException{ projectRepository = new ProjectRepository(db); } - public VulnerabilityConnector(Supplier cclient, Supplier hclient, String vmdbName, String dbName, String attchmntDbName) throws IOException { - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cclient, dbName); + public VulnerabilityConnector(Cloudant client, String vmdbName, String dbName, String attchmntDbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); - vulnerabilityDatabaseHandler = new VulnerabilityDatabaseHandler(hclient, vmdbName); - projectDatabaseHandler = new ProjectDatabaseHandler(cclient, dbName, attchmntDbName); + vulnerabilityDatabaseHandler = new VulnerabilityDatabaseHandler(client, vmdbName); + projectDatabaseHandler = new ProjectDatabaseHandler(client, dbName, attchmntDbName); vendorRepository = new VendorRepository(db); releaseRepository = new ReleaseRepository(db, vendorRepository); componentRepository = new ComponentRepository(db, releaseRepository, vendorRepository); diff --git a/backend/src/src-cvesearch/src/test/java/org/eclipse/sw360/cvesearch/datasink/VulnerabilityConnectorTest.java b/backend/src/src-cvesearch/src/test/java/org/eclipse/sw360/cvesearch/datasink/VulnerabilityConnectorTest.java index 2f494e6efd..8b98778e65 100644 --- a/backend/src/src-cvesearch/src/test/java/org/eclipse/sw360/cvesearch/datasink/VulnerabilityConnectorTest.java +++ b/backend/src/src-cvesearch/src/test/java/org/eclipse/sw360/cvesearch/datasink/VulnerabilityConnectorTest.java @@ -39,7 +39,7 @@ public class VulnerabilityConnectorTest { @Before public void setUp() throws Exception { - vulnerabilityConnector = new VulnerabilityConnector(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.getConfiguredHttpClient(), DatabaseSettingsTest.COUCH_DB_VM, DatabaseSettingsTest.COUCH_DB_DATABASE, DatabaseSettingsTest.COUCH_DB_ATTACHMENTS); + vulnerabilityConnector = new VulnerabilityConnector(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.COUCH_DB_VM, DatabaseSettingsTest.COUCH_DB_DATABASE, DatabaseSettingsTest.COUCH_DB_ATTACHMENTS); vulnerabilityConnector.vulnerabilityDatabaseHandler = vulnerabilityDatabaseHandler; statusToVulnerabilityMap = new HashMap<>(); for (UpdateType updateType: UpdateType.values()){ diff --git a/backend/src/src-health/src/main/java/org/eclipse/sw360/health/HealthHandler.java b/backend/src/src-health/src/main/java/org/eclipse/sw360/health/HealthHandler.java index 2b6e1eb4b0..037b8d368b 100644 --- a/backend/src/src-health/src/main/java/org/eclipse/sw360/health/HealthHandler.java +++ b/backend/src/src-health/src/main/java/org/eclipse/sw360/health/HealthHandler.java @@ -9,15 +9,14 @@ */ package org.eclipse.sw360.health; +import com.ibm.cloud.cloudant.v1.Cloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettings; import org.eclipse.sw360.datahandler.thrift.health.Health; import org.eclipse.sw360.datahandler.thrift.health.HealthService; import org.eclipse.sw360.health.db.HealthDatabaseHandler; -import org.ektorp.http.HttpClient; import java.net.MalformedURLException; import java.util.Set; -import java.util.function.Supplier; /** * Implementation of the thrift service "Health" @@ -27,11 +26,11 @@ public class HealthHandler implements HealthService.Iface { private final HealthDatabaseHandler handler; HealthHandler() throws MalformedURLException { - handler = new HealthDatabaseHandler(DatabaseSettings.getConfiguredHttpClient()); + handler = new HealthDatabaseHandler(DatabaseSettings.getConfiguredClient()); } - HealthHandler(Supplier httpClient) throws MalformedURLException { - handler = new HealthDatabaseHandler(httpClient); + HealthHandler(Cloudant client) throws MalformedURLException { + handler = new HealthDatabaseHandler(client); } @Override diff --git a/backend/src/src-health/src/main/java/org/eclipse/sw360/health/db/HealthDatabaseHandler.java b/backend/src/src-health/src/main/java/org/eclipse/sw360/health/db/HealthDatabaseHandler.java index 299086a83b..a034e8f9e2 100644 --- a/backend/src/src-health/src/main/java/org/eclipse/sw360/health/db/HealthDatabaseHandler.java +++ b/backend/src/src-health/src/main/java/org/eclipse/sw360/health/db/HealthDatabaseHandler.java @@ -10,29 +10,28 @@ package org.eclipse.sw360.health.db; import com.google.common.collect.ImmutableSet; +import com.ibm.cloud.cloudant.v1.Cloudant; +import com.ibm.cloud.sdk.core.service.exception.ServiceUnavailableException; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettings; -import org.eclipse.sw360.datahandler.couchdb.DatabaseInstance; import org.eclipse.sw360.datahandler.thrift.health.Health; import org.eclipse.sw360.datahandler.thrift.health.Status; -import org.ektorp.DbAccessException; -import org.ektorp.http.HttpClient; import java.net.MalformedURLException; import java.util.HashMap; import java.util.Set; -import java.util.function.Supplier; public class HealthDatabaseHandler { - private final DatabaseInstance db; + private final DatabaseInstanceCloudant db; public static final Set DATABASES_TO_CHECK = ImmutableSet.of( DatabaseSettings.COUCH_DB_ATTACHMENTS, DatabaseSettings.COUCH_DB_DATABASE, DatabaseSettings.COUCH_DB_USERS); - public HealthDatabaseHandler(Supplier httpClient) throws MalformedURLException { - db = new DatabaseInstance(httpClient.get()); + public HealthDatabaseHandler(Cloudant client) throws MalformedURLException { + db = new DatabaseInstanceCloudant(client); } public Health getHealth() { @@ -47,7 +46,7 @@ private Health getHealthOfDbs(Set dbsTocheck) { if (!db.checkIfDbExists(database)) { health.getDetails().put(database, String.format("The database '%s' does not exist.", database)); } - } catch (DbAccessException e) { + } catch (ServiceUnavailableException e) { health.getDetails().put(database, e.getMessage()); } } diff --git a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/LicenseHandler.java b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/LicenseHandler.java index dddb54fdbb..079a478e4a 100644 --- a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/LicenseHandler.java +++ b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/LicenseHandler.java @@ -11,6 +11,7 @@ package org.eclipse.sw360.licenses; +import com.ibm.cloud.cloudant.v1.Cloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettings; import org.eclipse.sw360.datahandler.db.ObligationSearchHandler; import org.eclipse.sw360.datahandler.permissions.PermissionUtils; @@ -53,8 +54,8 @@ public class LicenseHandler implements LicenseService.Iface { obligationSearchHandler = new ObligationSearchHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE); } - LicenseHandler(Supplier httpClient, String dbName) throws IOException { - handler = new LicenseDatabaseHandler(httpClient, dbName); + LicenseHandler(Cloudant client, String dbName) throws IOException { + handler = new LicenseDatabaseHandler(client, dbName); } ///////////////////// diff --git a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseDatabaseHandler.java b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseDatabaseHandler.java index b18c570752..925abe14f8 100644 --- a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseDatabaseHandler.java +++ b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseDatabaseHandler.java @@ -10,6 +10,7 @@ */ package org.eclipse.sw360.licenses.db; +import com.ibm.cloud.cloudant.v1.model.DocumentResult; import org.apache.commons.io.IOUtils; import org.apache.thrift.TException; import org.eclipse.sw360.components.summary.SummaryType; @@ -29,7 +30,6 @@ import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.users.UserGroup; -import org.eclipse.sw360.datahandler.thrift.changelogs.ChangeLogs; import org.eclipse.sw360.datahandler.thrift.changelogs.Operation; import org.eclipse.sw360.licenses.tools.SpdxConnector; import org.eclipse.sw360.exporter.LicenseExporter; @@ -37,13 +37,11 @@ import org.apache.http.HttpStatus; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.ektorp.DocumentOperationResult; import org.jetbrains.annotations.NotNull; import org.json.JSONArray; import org.json.JSONObject; -import com.cloudant.client.api.CloudantClient; -import com.cloudant.client.api.model.Response; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.common.collect.Sets; import static com.google.common.base.Strings.isNullOrEmpty; @@ -55,7 +53,6 @@ import java.sql.Timestamp; import java.time.Instant; import java.util.*; -import java.util.function.Supplier; import java.util.stream.Collectors; import static org.eclipse.sw360.datahandler.common.CommonUtils.*; @@ -100,10 +97,10 @@ public class LicenseDatabaseHandler { private String obligationText; private final Logger log = LogManager.getLogger(LicenseDatabaseHandler.class); - public LicenseDatabaseHandler(Supplier httpClient, String dbName) throws MalformedURLException { + public LicenseDatabaseHandler(Cloudant client, String dbName) throws MalformedURLException { // Create the connector - db = new DatabaseConnectorCloudant(httpClient, dbName); - DatabaseConnectorCloudant dbChangelogs = new DatabaseConnectorCloudant(httpClient, DatabaseSettings.COUCH_DB_CHANGE_LOGS); + db = new DatabaseConnectorCloudant(client, dbName); + DatabaseConnectorCloudant dbChangelogs = new DatabaseConnectorCloudant(client, DatabaseSettings.COUCH_DB_CHANGE_LOGS); dbHandlerUtil = new DatabaseHandlerUtil(dbChangelogs); // Create the repository @@ -730,9 +727,9 @@ public List addLicenseTypes(List licenseTypes, User us if (!PermissionUtils.isUserAtLeast(UserGroup.CLEARING_ADMIN, user)){ return null; } - List documentOperationResults = licenseTypeRepository.executeBulk(licenseTypes); + List documentOperationResults = licenseTypeRepository.executeBulk(licenseTypes); documentOperationResults = documentOperationResults.stream() - .filter(res -> res.getError() != null || res.getStatusCode() != HttpStatus.SC_CREATED).collect(Collectors.toList()); + .filter(res -> res.getError() != null || !res.isOk()).collect(Collectors.toList()); if (documentOperationResults.isEmpty()) { return licenseTypes; } else return null; @@ -762,9 +759,9 @@ public List addOrOverwriteLicenses(List licenses, User user, b prepareLicense(license); } - List documentOperationResults = licenseRepository.executeBulk(licenses); + List documentOperationResults = licenseRepository.executeBulk(licenses); documentOperationResults = documentOperationResults.stream() - .filter(res -> res.getError() != null || res.getStatusCode() != HttpStatus.SC_CREATED).collect(Collectors.toList()); + .filter(res -> res.getError() != null || !res.isOk()).collect(Collectors.toList()); if (documentOperationResults.isEmpty()) { return licenses; } else { @@ -782,9 +779,9 @@ public List addListOfObligations(List listOfObligations, prepareTodo(Oblig); } - List documentOperationResults = obligRepository.executeBulk(listOfObligations); + List documentOperationResults = obligRepository.executeBulk(listOfObligations); documentOperationResults = documentOperationResults.stream() - .filter(res -> res.getError() != null || res.getStatusCode() != HttpStatus.SC_CREATED).collect(Collectors.toList()); + .filter(res -> res.getError() != null || !res.isOk()).collect(Collectors.toList()); if (documentOperationResults.isEmpty()) { return listOfObligations; } else return null; @@ -1002,7 +999,7 @@ public RequestSummary deleteAllLicenseInformation() { private RequestSummary deleteAllDocuments(DatabaseRepositoryCloudantClient repository) { Set allIds = repository.getAllIds(); - List operationResults = repository.deleteIds(allIds); + List operationResults = repository.deleteIds(allIds); return getRequestSummary(allIds.size(), operationResults.size()); } diff --git a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseObligationListRepository.java b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseObligationListRepository.java index 0fbcf655dc..14797b77c0 100644 --- a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseObligationListRepository.java +++ b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseObligationListRepository.java @@ -17,7 +17,7 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.licenses.LicenseObligationList; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; /** * CRUD access for the LicenseObligationList class @@ -37,7 +37,7 @@ public class LicenseObligationListRepository extends DatabaseRepositoryCloudantC public LicenseObligationListRepository(DatabaseConnectorCloudant db) { super(db, LicenseObligationList.class); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("byLicenseId", createMapReduce(BY_LICENSE_ID, null)); views.put("all", createMapReduce(ALL, null)); initStandardDesignDocument(views, db); diff --git a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseRepository.java b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseRepository.java index c94ba1d74a..9c82edca81 100644 --- a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseRepository.java +++ b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseRepository.java @@ -15,7 +15,7 @@ import org.eclipse.sw360.datahandler.couchdb.SummaryAwareRepository; import org.eclipse.sw360.datahandler.thrift.licenses.License; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import java.util.HashMap; import java.util.List; @@ -36,7 +36,7 @@ public class LicenseRepository extends SummaryAwareRepository { public LicenseRepository(DatabaseConnectorCloudant db) { super(License.class, db, new LicenseSummary()); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); views.put("byname", createMapReduce(BYNAME, null)); views.put("byshortname", createMapReduce(BYSHORTNAME, null)); diff --git a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseTypeRepository.java b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseTypeRepository.java index c8c78ff50d..05e23c608b 100644 --- a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseTypeRepository.java +++ b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/LicenseTypeRepository.java @@ -16,7 +16,7 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.licenses.LicenseType; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import java.util.List; @@ -30,7 +30,7 @@ public class LicenseTypeRepository extends DatabaseRepositoryCloudantClient views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); views.put("bylicensetype", createMapReduce(BYLICENSETYPE, null)); initStandardDesignDocument(views, db); diff --git a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/ObligationElementRepository.java b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/ObligationElementRepository.java index 8e928a9082..47e839bc3a 100644 --- a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/ObligationElementRepository.java +++ b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/ObligationElementRepository.java @@ -18,7 +18,7 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.licenses.ObligationElement; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; /** * CRUD access for the Obligation Element class @@ -33,7 +33,7 @@ public class ObligationElementRepository extends DatabaseRepositoryCloudantClien public ObligationElementRepository(DatabaseConnectorCloudant db) { super(db, ObligationElement.class); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); views.put("byobligationlang", createMapReduce(BYLANGELEMENT, null)); views.put("byobligationaction", createMapReduce(BYACTION, null)); diff --git a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/ObligationNodeRepository.java b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/ObligationNodeRepository.java index 5e8daf1721..8d586e374c 100644 --- a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/ObligationNodeRepository.java +++ b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/ObligationNodeRepository.java @@ -18,7 +18,7 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.licenses.ObligationNode; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; /** * CRUD access for the Obligation Node class */ @@ -32,7 +32,7 @@ public class ObligationNodeRepository extends DatabaseRepositoryCloudantClient views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); views.put("byobligationnodetype", createMapReduce(BYNODETYPE, null)); views.put("byobligationnodetext", createMapReduce(BYNODETEXT, null)); diff --git a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/TodoRepository.java b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/TodoRepository.java index 391a2ffc5e..4e38ec1f50 100644 --- a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/TodoRepository.java +++ b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/db/TodoRepository.java @@ -16,7 +16,7 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.licenses.Obligation; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; /** * CRUD access for the Obligation class @@ -30,7 +30,7 @@ public class TodoRepository extends DatabaseRepositoryCloudantClient public TodoRepository(DatabaseConnectorCloudant db) { super(db, Obligation.class); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); initStandardDesignDocument(views, db); } diff --git a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ClearingRequestRepository.java b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ClearingRequestRepository.java index 6736842736..050b29fab7 100644 --- a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ClearingRequestRepository.java +++ b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ClearingRequestRepository.java @@ -15,7 +15,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; @@ -24,7 +23,7 @@ import org.eclipse.sw360.datahandler.thrift.ClearingRequestState; import org.eclipse.sw360.datahandler.thrift.projects.ClearingRequest; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; /** * CRUD access for the ClearingRequest class @@ -69,7 +68,7 @@ public class ClearingRequestRepository extends DatabaseRepositoryCloudantClient< public ClearingRequestRepository(DatabaseConnectorCloudant db) { super(db, ClearingRequest.class); - Map views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); views.put("byProjectId", createMapReduce(BY_PROJECT_ID, null)); views.put("myClearingRequests", createMapReduce(MY_CLEARING_REQUESTS, null)); diff --git a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/LicenseModerationRequestGenerator.java b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/LicenseModerationRequestGenerator.java index 3118bdb19a..f30511f1dd 100644 --- a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/LicenseModerationRequestGenerator.java +++ b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/LicenseModerationRequestGenerator.java @@ -54,7 +54,7 @@ public ModerationRequest setAdditionsAndDeletions(ModerationRequest request, Lic documentAdditions.getObligations().add(updateTodo); } else { Obligation actualTodo = actualTodos.get(updateTodo.getId()); - Set actualWhitelist = actualTodo.whitelist != null ? actualTodo.whitelist : new HashSet(); + Set actualWhitelist = actualTodo != null && actualTodo.whitelist != null ? actualTodo.whitelist : new HashSet(); Set updateWhitelist = updateTodo.whitelist != null ? updateTodo.whitelist : new HashSet(); String departement = request.getRequestingUserDepartment(); if(updateWhitelist.contains(departement) && !actualWhitelist.contains(departement)){ @@ -66,7 +66,9 @@ public ModerationRequest setAdditionsAndDeletions(ModerationRequest request, Lic if(!documentDeletions.isSetObligations()) { documentDeletions.setObligations(new ArrayList<>()); } - documentDeletions.getObligations().add(actualTodo); + if (actualTodo != null) { + documentDeletions.getObligations().add(actualTodo); + } } } } diff --git a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationDatabaseHandler.java b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationDatabaseHandler.java index 1e9297988e..b3954722c4 100644 --- a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationDatabaseHandler.java +++ b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationDatabaseHandler.java @@ -10,7 +10,7 @@ package org.eclipse.sw360.moderation.db; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -67,7 +67,6 @@ import java.util.Map.Entry; import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -104,26 +103,26 @@ public class ModerationDatabaseHandler { private final MailUtil mailUtil = new MailUtil(); - public ModerationDatabaseHandler(Supplier httpClient, String dbName, String attachmentDbName) throws MalformedURLException { - db = new DatabaseConnectorCloudant(httpClient, dbName); + public ModerationDatabaseHandler(Cloudant client, String dbName, String attachmentDbName) throws MalformedURLException { + db = new DatabaseConnectorCloudant(client, dbName); // Create the repository repository = new ModerationRequestRepository(db); clearingRequestRepository = new ClearingRequestRepository(db); - licenseDatabaseHandler = new LicenseDatabaseHandler(httpClient, dbName); - projectDatabaseHandler = new ProjectDatabaseHandler(httpClient, dbName, attachmentDbName); - componentDatabaseHandler = new ComponentDatabaseHandler(httpClient, dbName, attachmentDbName); - spdxDocumentDatabaseHandler = new SpdxDocumentDatabaseHandler(httpClient, DatabaseSettings.COUCH_DB_SPDX); - spdxDocumentCreationInfoDatabaseHandler = new SpdxDocumentCreationInfoDatabaseHandler(httpClient, DatabaseSettings.COUCH_DB_SPDX); - spdxPackageInfoDatabaseHandler = new SpdxPackageInfoDatabaseHandler(httpClient, DatabaseSettings.COUCH_DB_SPDX); - DatabaseConnectorCloudant dbChangeLogs = new DatabaseConnectorCloudant(httpClient, DatabaseSettings.COUCH_DB_CHANGE_LOGS); + licenseDatabaseHandler = new LicenseDatabaseHandler(client, dbName); + projectDatabaseHandler = new ProjectDatabaseHandler(client, dbName, attachmentDbName); + componentDatabaseHandler = new ComponentDatabaseHandler(client, dbName, attachmentDbName); + spdxDocumentDatabaseHandler = new SpdxDocumentDatabaseHandler(client, DatabaseSettings.COUCH_DB_SPDX); + spdxDocumentCreationInfoDatabaseHandler = new SpdxDocumentCreationInfoDatabaseHandler(client, DatabaseSettings.COUCH_DB_SPDX); + spdxPackageInfoDatabaseHandler = new SpdxPackageInfoDatabaseHandler(client, DatabaseSettings.COUCH_DB_SPDX); + DatabaseConnectorCloudant dbChangeLogs = new DatabaseConnectorCloudant(client, DatabaseSettings.COUCH_DB_CHANGE_LOGS); this.dbHandlerUtil = new DatabaseHandlerUtil(dbChangeLogs); } - public ModerationDatabaseHandler(Supplier httpClient, String dbName, String changeLogsDbName, String attachmentDbName) throws MalformedURLException { - this(httpClient, dbName, attachmentDbName); - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(httpClient, changeLogsDbName); + public ModerationDatabaseHandler(Cloudant client, String dbName, String changeLogsDbName, String attachmentDbName) throws MalformedURLException { + this(client, dbName, attachmentDbName); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, changeLogsDbName); this.dbHandlerUtil = new DatabaseHandlerUtil(db); } diff --git a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationRequestRepository.java b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationRequestRepository.java index f82135ce2a..4c00ab8899 100644 --- a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationRequestRepository.java +++ b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationRequestRepository.java @@ -32,7 +32,7 @@ import org.eclipse.sw360.datahandler.thrift.PaginationData; import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; -import com.cloudant.client.api.model.DesignDocument.MapReduce; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import com.cloudant.client.api.query.Expression; import com.cloudant.client.api.query.PredicateExpression; import com.cloudant.client.api.query.PredicatedOperation; @@ -83,7 +83,7 @@ public class ModerationRequestRepository extends SummaryAwareRepository views = new HashMap(); + Map views = new HashMap<>(); views.put("all", createMapReduce(ALL, null)); views.put("byRequestingUsersDeptView", createMapReduce(REQUESTING_USERS_VIEW, null)); views.put("countByModerationState", createMapReduce(COUNTBYMODERATIONSTATE, "_count")); diff --git a/backend/src/src-moderation/src/test/java/org/eclipse/sw360/moderation/testutil/DatabaseTestSetup.java b/backend/src/src-moderation/src/test/java/org/eclipse/sw360/moderation/testutil/DatabaseTestSetup.java index 88566818be..e8eb71b767 100644 --- a/backend/src/src-moderation/src/test/java/org/eclipse/sw360/moderation/testutil/DatabaseTestSetup.java +++ b/backend/src/src-moderation/src/test/java/org/eclipse/sw360/moderation/testutil/DatabaseTestSetup.java @@ -11,7 +11,6 @@ package org.eclipse.sw360.moderation.testutil; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.thrift.moderation.DocumentType; import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; import org.eclipse.sw360.datahandler.thrift.projects.Project; diff --git a/backend/src/src-projects/src/main/java/org/eclipse/sw360/projects/ProjectHandler.java b/backend/src/src-projects/src/main/java/org/eclipse/sw360/projects/ProjectHandler.java index d54adee445..8eb467d16d 100644 --- a/backend/src/src-projects/src/main/java/org/eclipse/sw360/projects/ProjectHandler.java +++ b/backend/src/src-projects/src/main/java/org/eclipse/sw360/projects/ProjectHandler.java @@ -42,12 +42,11 @@ import org.eclipse.sw360.datahandler.thrift.projects.UsedReleaseRelations; import org.eclipse.sw360.datahandler.thrift.users.User; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import java.io.IOException; import java.nio.ByteBuffer; import java.util.*; -import java.util.function.Supplier; /** * Implementation of the Thrift service @@ -69,14 +68,14 @@ public class ProjectHandler implements ProjectService.Iface { searchHandler = new ProjectSearchHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE); } - ProjectHandler(Supplier httpClient, String dbName, String attchmntDbName) throws IOException { - handler = new ProjectDatabaseHandler(httpClient, dbName, attchmntDbName); + ProjectHandler(Cloudant client, String dbName, String attchmntDbName) throws IOException { + handler = new ProjectDatabaseHandler(client, dbName, attchmntDbName); searchHandler = new ProjectSearchHandler(DatabaseSettings.getConfiguredClient(), dbName); } - ProjectHandler(Supplier cClient, String dbName, String changeLogsDbName, String attchmntDbName) throws IOException { - handler = new ProjectDatabaseHandler(cClient, dbName, changeLogsDbName, attchmntDbName); - searchHandler = new ProjectSearchHandler(cClient, dbName); + ProjectHandler(Cloudant client, String dbName, String changeLogsDbName, String attchmntDbName) throws IOException { + handler = new ProjectDatabaseHandler(client, dbName, changeLogsDbName, attchmntDbName); + searchHandler = new ProjectSearchHandler(client, dbName); } ///////////////////// diff --git a/backend/src/src-projects/src/test/java/org/eclipse/sw360/projects/ProjectHandlerTest.java b/backend/src/src-projects/src/test/java/org/eclipse/sw360/projects/ProjectHandlerTest.java index 9c3a3b18aa..447756b511 100644 --- a/backend/src/src-projects/src/test/java/org/eclipse/sw360/projects/ProjectHandlerTest.java +++ b/backend/src/src-projects/src/test/java/org/eclipse/sw360/projects/ProjectHandlerTest.java @@ -14,7 +14,6 @@ import org.eclipse.sw360.datahandler.TestUtils; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; import org.eclipse.sw360.datahandler.common.SW360Utils; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.db.AttachmentDatabaseHandler; import org.eclipse.sw360.datahandler.db.ComponentDatabaseHandler; import org.eclipse.sw360.datahandler.db.PackageDatabaseHandler; diff --git a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/SearchHandler.java b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/SearchHandler.java index b486185508..380b588052 100644 --- a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/SearchHandler.java +++ b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/SearchHandler.java @@ -13,6 +13,7 @@ import com.cloudant.client.api.CloudantClient; import com.google.common.collect.ImmutableMap; +import com.ibm.cloud.cloudant.v1.Cloudant; import org.apache.jena.ext.com.google.common.collect.Sets; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -54,9 +55,9 @@ public SearchHandler() throws IOException { dbSw360users = new Sw360usersDatabaseSearchHandler(); } - public SearchHandler(Supplier cclient, String dbName) throws IOException { - dbSw360db = new Sw360dbDatabaseSearchHandler(cclient, dbName); - dbSw360users = new Sw360usersDatabaseSearchHandler(cclient, dbName); + public SearchHandler(Cloudant client, String dbName) throws IOException { + dbSw360db = new Sw360dbDatabaseSearchHandler(client, dbName); + dbSw360users = new Sw360usersDatabaseSearchHandler(client, dbName); } @Override diff --git a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/Sw360usersDatabaseSearchHandler.java b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/Sw360usersDatabaseSearchHandler.java index f503f0d2d5..b3b151a8df 100644 --- a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/Sw360usersDatabaseSearchHandler.java +++ b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/Sw360usersDatabaseSearchHandler.java @@ -14,10 +14,9 @@ import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.search.db.AbstractDatabaseSearchHandler; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import java.io.IOException; -import java.util.function.Supplier; public class Sw360usersDatabaseSearchHandler extends AbstractDatabaseSearchHandler { @@ -25,7 +24,7 @@ public Sw360usersDatabaseSearchHandler() throws IOException { super(DatabaseSettings.COUCH_DB_USERS); } - public Sw360usersDatabaseSearchHandler(Supplier client, String dbName) throws IOException { + public Sw360usersDatabaseSearchHandler(Cloudant client, String dbName) throws IOException { super(client, dbName); } diff --git a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java index ec466286ec..4280121833 100644 --- a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java +++ b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java @@ -10,13 +10,12 @@ */ package org.eclipse.sw360.search.db; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.collect.FluentIterable; import com.google.gson.Gson; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettings; import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.search.SearchResult; @@ -30,7 +29,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.function.Supplier; import static org.eclipse.sw360.common.utils.SearchUtils.OBJ_TO_DEFAULT_INDEX; import static org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector.prepareWildcardQuery; @@ -84,11 +82,11 @@ public abstract class AbstractDatabaseSearchHandler { private final NouveauLuceneAwareDatabaseConnector connector; public AbstractDatabaseSearchHandler(String dbName) throws IOException { - Supplier cClient = DatabaseSettings.getConfiguredClient(); - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); + Cloudant client = DatabaseSettings.getConfiguredClient(); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); // Create the database connector and add the search view to couchDB - connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); - Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); searchView.addNouveau(luceneSearchView, gson); @@ -97,11 +95,11 @@ public AbstractDatabaseSearchHandler(String dbName) throws IOException { connector.addDesignDoc(searchView); } - public AbstractDatabaseSearchHandler(Supplier cClient, String dbName) throws IOException { - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); + public AbstractDatabaseSearchHandler(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); // Create the database connector and add the search view to couchDB - connector = new NouveauLuceneAwareDatabaseConnector(db, cClient, DDOC_NAME); - Gson gson = (new DatabaseInstanceCloudant(cClient)).getClient().getGson(); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); searchView.addNouveau(luceneSearchView, gson); diff --git a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/Sw360dbDatabaseSearchHandler.java b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/Sw360dbDatabaseSearchHandler.java index 930ec7d6af..7604977100 100644 --- a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/Sw360dbDatabaseSearchHandler.java +++ b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/Sw360dbDatabaseSearchHandler.java @@ -26,10 +26,9 @@ import org.eclipse.sw360.datahandler.thrift.search.SearchResult; import org.eclipse.sw360.datahandler.thrift.users.User; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import java.io.IOException; -import java.util.function.Supplier; public class Sw360dbDatabaseSearchHandler extends AbstractDatabaseSearchHandler { @@ -50,10 +49,10 @@ public Sw360dbDatabaseSearchHandler() throws IOException { componentRepository = new ComponentRepository(db, releaseRepository, vendorRepository); } - public Sw360dbDatabaseSearchHandler(Supplier cclient, String dbName) throws IOException { - super(cclient, dbName); + public Sw360dbDatabaseSearchHandler(Cloudant client, String dbName) throws IOException { + super(client, dbName); - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cclient, dbName); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); projectRepository = new ProjectRepository(db); vendorRepository = new VendorRepository(db); diff --git a/backend/src/src-spdxdocument/src/main/java/org/eclipse/sw360/spdxdocument/SPDXDocumentHandler.java b/backend/src/src-spdxdocument/src/main/java/org/eclipse/sw360/spdxdocument/SPDXDocumentHandler.java index 9b0512c714..1447133045 100644 --- a/backend/src/src-spdxdocument/src/main/java/org/eclipse/sw360/spdxdocument/SPDXDocumentHandler.java +++ b/backend/src/src-spdxdocument/src/main/java/org/eclipse/sw360/spdxdocument/SPDXDocumentHandler.java @@ -17,13 +17,12 @@ import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.*; import org.eclipse.sw360.datahandler.thrift.users.User; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import org.apache.thrift.TException; import java.net.MalformedURLException; import java.util.List; -import java.util.function.Supplier; import static org.eclipse.sw360.datahandler.common.SW360Assert.*; @@ -35,8 +34,8 @@ public class SPDXDocumentHandler implements SPDXDocumentService.Iface { handler = new SpdxDocumentDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); } - SPDXDocumentHandler(Supplier httpClient, String dbName) throws MalformedURLException { - handler = new SpdxDocumentDatabaseHandler(httpClient, dbName); + SPDXDocumentHandler(Cloudant client, String dbName) throws MalformedURLException { + handler = new SpdxDocumentDatabaseHandler(client, dbName); } @Override diff --git a/backend/src/src-spdxdocumentcreationinfo/src/main/java/org/eclipse/sw360/spdxdocumentcreationinfo/DocumentCreationInformationHandler.java b/backend/src/src-spdxdocumentcreationinfo/src/main/java/org/eclipse/sw360/spdxdocumentcreationinfo/DocumentCreationInformationHandler.java index a094bb1247..bfa3bcbd8c 100644 --- a/backend/src/src-spdxdocumentcreationinfo/src/main/java/org/eclipse/sw360/spdxdocumentcreationinfo/DocumentCreationInformationHandler.java +++ b/backend/src/src-spdxdocumentcreationinfo/src/main/java/org/eclipse/sw360/spdxdocumentcreationinfo/DocumentCreationInformationHandler.java @@ -17,13 +17,12 @@ import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.*; import org.eclipse.sw360.datahandler.thrift.users.User; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import org.apache.thrift.TException; import java.net.MalformedURLException; import java.util.List; -import java.util.function.Supplier; import static org.eclipse.sw360.datahandler.common.SW360Assert.*; @@ -35,8 +34,8 @@ public class DocumentCreationInformationHandler implements DocumentCreationInfor handler = new SpdxDocumentCreationInfoDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); } - DocumentCreationInformationHandler(Supplier httpClient, String dbName) throws MalformedURLException { - handler = new SpdxDocumentCreationInfoDatabaseHandler(httpClient, dbName); + DocumentCreationInformationHandler(Cloudant client, String dbName) throws MalformedURLException { + handler = new SpdxDocumentCreationInfoDatabaseHandler(client, dbName); } @Override diff --git a/backend/src/src-spdxpackageinfo/src/main/java/org/eclipse/sw360/spdxpackageinfo/PackageInformationHandler.java b/backend/src/src-spdxpackageinfo/src/main/java/org/eclipse/sw360/spdxpackageinfo/PackageInformationHandler.java index ce8d71ea4a..d14defd138 100644 --- a/backend/src/src-spdxpackageinfo/src/main/java/org/eclipse/sw360/spdxpackageinfo/PackageInformationHandler.java +++ b/backend/src/src-spdxpackageinfo/src/main/java/org/eclipse/sw360/spdxpackageinfo/PackageInformationHandler.java @@ -18,14 +18,13 @@ import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.*; import org.eclipse.sw360.datahandler.thrift.users.User; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import org.apache.thrift.TException; import java.net.MalformedURLException; import java.util.List; import java.util.Set; -import java.util.function.Supplier; import static org.eclipse.sw360.datahandler.common.SW360Assert.*; @@ -37,8 +36,8 @@ public class PackageInformationHandler implements PackageInformationService.Ifac handler = new SpdxPackageInfoDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); } - PackageInformationHandler(Supplier httpClient, String dbName) throws MalformedURLException { - handler = new SpdxPackageInfoDatabaseHandler(httpClient, dbName); + PackageInformationHandler(Cloudant client, String dbName) throws MalformedURLException { + handler = new SpdxPackageInfoDatabaseHandler(client, dbName); } @Override diff --git a/backend/src/src-users/src/main/java/org/eclipse/sw360/users/UserHandler.java b/backend/src/src-users/src/main/java/org/eclipse/sw360/users/UserHandler.java index 160cede59d..01239d5a36 100644 --- a/backend/src/src-users/src/main/java/org/eclipse/sw360/users/UserHandler.java +++ b/backend/src/src-users/src/main/java/org/eclipse/sw360/users/UserHandler.java @@ -9,7 +9,7 @@ */ package org.eclipse.sw360.users; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import org.apache.commons.io.FilenameUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -30,7 +30,6 @@ import java.io.File; import java.io.IOException; import java.util.*; -import java.util.function.Supplier; import static org.eclipse.sw360.datahandler.common.SW360Assert.assertNotEmpty; import static org.eclipse.sw360.datahandler.common.SW360Assert.assertNotNull; @@ -54,7 +53,7 @@ public UserHandler() throws IOException { readFileDepartmentConfig = new ReadFileDepartmentConfig(); } - public UserHandler(Supplier client, String userDbName) throws IOException { + public UserHandler(Cloudant client, String userDbName) throws IOException { db = new UserDatabaseHandler(client, userDbName); } diff --git a/backend/src/src-users/src/main/java/org/eclipse/sw360/users/db/UserDatabaseHandler.java b/backend/src/src-users/src/main/java/org/eclipse/sw360/users/db/UserDatabaseHandler.java index 79f9299581..c112fc8e9a 100644 --- a/backend/src/src-users/src/main/java/org/eclipse/sw360/users/db/UserDatabaseHandler.java +++ b/backend/src/src-users/src/main/java/org/eclipse/sw360/users/db/UserDatabaseHandler.java @@ -9,7 +9,7 @@ */ package org.eclipse.sw360.users.db; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.opencsv.CSVReader; @@ -22,7 +22,6 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.common.DatabaseSettings; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.db.UserRepository; import org.eclipse.sw360.datahandler.db.UserSearchHandler; import org.eclipse.sw360.datahandler.thrift.*; @@ -37,7 +36,6 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; -import java.util.function.Supplier; import java.util.stream.Collectors; import static org.eclipse.sw360.datahandler.permissions.PermissionUtils.makePermission; @@ -55,7 +53,6 @@ public class UserDatabaseHandler { * Connection to the couchDB database */ private DatabaseConnectorCloudant db; - private DatabaseConnector dbConnector; private UserRepository repository; private UserSearchHandler userSearchHandler; private static final Logger log = LogManager.getLogger(UserDatabaseHandler.class); @@ -68,10 +65,9 @@ public class UserDatabaseHandler { private List emailDoNotExist; DateFormat dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH); - public UserDatabaseHandler(Supplier httpClient, String dbName) throws IOException { + public UserDatabaseHandler(Cloudant client, String dbName) throws IOException { // Create the connector - db = new DatabaseConnectorCloudant(httpClient, dbName); - dbConnector = new DatabaseConnector(DatabaseSettings.getConfiguredHttpClient(), dbName); + db = new DatabaseConnectorCloudant(client, dbName); repository = new UserRepository(db); readFileDepartmentConfig = new ReadFileDepartmentConfig(); userSearchHandler = new UserSearchHandler(DatabaseSettings.getConfiguredClient(), dbName); diff --git a/backend/src/src-vendors/src/main/java/org/eclipse/sw360/vendors/VendorDatabaseHandler.java b/backend/src/src-vendors/src/main/java/org/eclipse/sw360/vendors/VendorDatabaseHandler.java index 68b0ba0bdb..73def30a8e 100644 --- a/backend/src/src-vendors/src/main/java/org/eclipse/sw360/vendors/VendorDatabaseHandler.java +++ b/backend/src/src-vendors/src/main/java/org/eclipse/sw360/vendors/VendorDatabaseHandler.java @@ -40,9 +40,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.function.Supplier; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import com.google.common.collect.ImmutableSet; import static org.eclipse.sw360.datahandler.common.SW360Assert.assertNotNull; @@ -53,8 +52,8 @@ public class VendorDatabaseHandler { private static final Logger log = LogManager.getLogger(VendorDatabaseHandler.class); private final VendorRepository repository; - public VendorDatabaseHandler(Supplier httpClient, String dbName) throws MalformedURLException { - DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(httpClient, dbName); + public VendorDatabaseHandler(Cloudant client, String dbName) throws MalformedURLException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); repository = new VendorRepository(db); } diff --git a/backend/src/src-vendors/src/main/java/org/eclipse/sw360/vendors/VendorHandler.java b/backend/src/src-vendors/src/main/java/org/eclipse/sw360/vendors/VendorHandler.java index c491838790..f01678e608 100644 --- a/backend/src/src-vendors/src/main/java/org/eclipse/sw360/vendors/VendorHandler.java +++ b/backend/src/src-vendors/src/main/java/org/eclipse/sw360/vendors/VendorHandler.java @@ -12,23 +12,20 @@ import org.apache.thrift.TException; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettings; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.db.VendorSearchHandler; import org.eclipse.sw360.datahandler.thrift.AddDocumentRequestSummary; import org.eclipse.sw360.datahandler.thrift.RequestStatus; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; import org.eclipse.sw360.datahandler.thrift.vendors.VendorService; -import org.ektorp.http.HttpClient; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import java.io.IOException; import java.nio.ByteBuffer; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.function.Supplier; import static org.eclipse.sw360.datahandler.common.SW360Assert.*; public class VendorHandler implements VendorService.Iface { @@ -38,16 +35,14 @@ public class VendorHandler implements VendorService.Iface { public VendorHandler() throws IOException { DatabaseConnectorCloudant databaseConnector = new DatabaseConnectorCloudant(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE); - DatabaseConnector databaseConnectorNative = new DatabaseConnector(DatabaseSettings.getConfiguredHttpClient(), DatabaseSettings.COUCH_DB_DATABASE); vendorDatabaseHandler = new VendorDatabaseHandler(databaseConnector); - vendorSearchHandler = new VendorSearchHandler(databaseConnectorNative, DatabaseSettings.getConfiguredClient()); // Remove release id from component + vendorSearchHandler = new VendorSearchHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_DATABASE); // Remove release id from component } - public VendorHandler(Supplier httpClient,Supplier clientlient, String dbName) throws IOException { - DatabaseConnectorCloudant databaseConnector = new DatabaseConnectorCloudant(httpClient, dbName); - DatabaseConnector databaseConnectorNative = new DatabaseConnector(clientlient, DatabaseSettings.COUCH_DB_DATABASE); + public VendorHandler(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant databaseConnector = new DatabaseConnectorCloudant(client, dbName); vendorDatabaseHandler = new VendorDatabaseHandler(databaseConnector); - vendorSearchHandler = new VendorSearchHandler(databaseConnectorNative, httpClient); // Remove release id from component + vendorSearchHandler = new VendorSearchHandler(client, DatabaseSettings.COUCH_DB_DATABASE); // Remove release id from component } @Override diff --git a/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/TestVendorClient.java b/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/TestVendorClient.java index 72070582b0..68ce740b09 100644 --- a/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/TestVendorClient.java +++ b/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/TestVendorClient.java @@ -10,7 +10,6 @@ package org.eclipse.sw360.vendors; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; import org.eclipse.sw360.datahandler.thrift.vendors.VendorService; import org.apache.thrift.TException; diff --git a/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/VendorHandlerTest.java b/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/VendorHandlerTest.java index 5b86901af7..7b9c27316b 100644 --- a/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/VendorHandlerTest.java +++ b/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/VendorHandlerTest.java @@ -11,8 +11,6 @@ import org.eclipse.sw360.datahandler.TestUtils; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.DatabaseInstance; import org.eclipse.sw360.datahandler.thrift.AddDocumentRequestSummary; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; import org.junit.After; diff --git a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMActionRepository.java b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMActionRepository.java index afd7c939a6..3e8758baac 100644 --- a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMActionRepository.java +++ b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMActionRepository.java @@ -5,7 +5,6 @@ package org.eclipse.sw360.vmcomponents.db; import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.couchdb.DatabaseRepository; import org.eclipse.sw360.datahandler.thrift.vmcomponents.VMAction; import org.ektorp.support.View; diff --git a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMComponentRepository.java b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMComponentRepository.java index 1b3dcaeab6..ca6942b581 100644 --- a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMComponentRepository.java +++ b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMComponentRepository.java @@ -5,7 +5,6 @@ package org.eclipse.sw360.vmcomponents.db; import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.couchdb.DatabaseRepository; import org.eclipse.sw360.datahandler.thrift.vmcomponents.VMComponent; import org.ektorp.support.View; diff --git a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMDatabaseHandler.java b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMDatabaseHandler.java index 845113997d..2b98dedd5c 100644 --- a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMDatabaseHandler.java +++ b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMDatabaseHandler.java @@ -4,23 +4,22 @@ */ package org.eclipse.sw360.vmcomponents.db; +import com.ibm.cloud.cloudant.v1.Cloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.thrift.vmcomponents.*; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.thrift.TBase; import org.eclipse.sw360.datahandler.common.DatabaseSettings; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.thrift.RequestStatus; import org.eclipse.sw360.vmcomponents.common.SVMMapper; import org.eclipse.sw360.vulnerabilities.db.VulnerabilityDatabaseHandler; -import org.ektorp.http.HttpClient; import java.net.MalformedURLException; import java.util.Collection; import java.util.List; import java.util.Set; -import java.util.function.Supplier; /** * Class for accessing the CouchDB database @@ -41,12 +40,12 @@ public class VMDatabaseHandler extends VulnerabilityDatabaseHandler { private VMMatchRepository matchRepo; public VMDatabaseHandler() throws MalformedURLException { - this(DatabaseSettings.getConfiguredHttpClient(), DatabaseSettings.COUCH_DB_VM); + this(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_VM); } - public VMDatabaseHandler(Supplier httpClient, String dbName) throws MalformedURLException { - super(httpClient, dbName); - DatabaseConnector db = new DatabaseConnector(httpClient, dbName); + public VMDatabaseHandler(Cloudant client, String dbName) throws MalformedURLException { + super(client, dbName); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); compRepo = new VMComponentRepository(db); actionRepo = new VMActionRepository(db); prioRepo = new VMPriorityRepository(db); diff --git a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMMatchRepository.java b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMMatchRepository.java index 468fd541f4..fabcefc110 100644 --- a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMMatchRepository.java +++ b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMMatchRepository.java @@ -6,7 +6,6 @@ import org.eclipse.sw360.datahandler.thrift.vmcomponents.VMMatch; import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.couchdb.DatabaseRepository; import org.ektorp.support.View; diff --git a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMPriorityRepository.java b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMPriorityRepository.java index 7234aa7680..29c73c1540 100644 --- a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMPriorityRepository.java +++ b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMPriorityRepository.java @@ -6,7 +6,6 @@ import org.eclipse.sw360.datahandler.thrift.vmcomponents.VMPriority; import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.couchdb.DatabaseRepository; import org.ektorp.support.View; diff --git a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMProcessReportingRepository.java b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMProcessReportingRepository.java index 72196380bc..1a71c593af 100644 --- a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMProcessReportingRepository.java +++ b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMProcessReportingRepository.java @@ -6,7 +6,6 @@ import org.eclipse.sw360.datahandler.thrift.vmcomponents.VMProcessReporting; import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.couchdb.DatabaseRepository; import org.ektorp.support.View; diff --git a/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/db/VMDatabaseHandlerTest.java b/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/db/VMDatabaseHandlerTest.java index a22ca7ad2a..8acb711fe5 100644 --- a/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/db/VMDatabaseHandlerTest.java +++ b/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/db/VMDatabaseHandlerTest.java @@ -8,7 +8,6 @@ import org.eclipse.sw360.datahandler.TestUtils; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; import org.eclipse.sw360.datahandler.common.SW360Utils; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.thrift.RequestStatus; import org.eclipse.sw360.datahandler.thrift.vmcomponents.*; diff --git a/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityDatabaseHandler.java b/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityDatabaseHandler.java index c09fcb3ee4..9cfd3ee2dd 100644 --- a/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityDatabaseHandler.java +++ b/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityDatabaseHandler.java @@ -12,16 +12,15 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; -import org.apache.thrift.TException; +import com.ibm.cloud.cloudant.v1.Cloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.common.DatabaseSettings; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.thrift.RequestStatus; import org.eclipse.sw360.datahandler.thrift.SW360Exception; import org.eclipse.sw360.datahandler.thrift.VerificationStateInfo; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.*; import org.eclipse.sw360.vulnerabilities.common.VulnerabilityMapper; -import org.ektorp.http.HttpClient; import org.apache.http.HttpStatus; import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.LogManager; @@ -30,7 +29,6 @@ import java.net.MalformedURLException; import java.util.*; -import java.util.function.Supplier; /** * Class for accessing the CouchDB database @@ -45,15 +43,15 @@ public class VulnerabilityDatabaseHandler { private final VulnerabilityRelationRepository relationRepo; public VulnerabilityDatabaseHandler() throws MalformedURLException { - DatabaseConnector db = new DatabaseConnector(DatabaseSettings.getConfiguredHttpClient(), + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_VM); vulRepo = new VulnerabilityRepository(db); relationRepo = new VulnerabilityRelationRepository(db); } - public VulnerabilityDatabaseHandler(Supplier client, String vmDbName) throws MalformedURLException { - DatabaseConnector db = new DatabaseConnector(client, vmDbName); + public VulnerabilityDatabaseHandler(Cloudant client, String vmDbName) throws MalformedURLException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, vmDbName); vulRepo = new VulnerabilityRepository(db); relationRepo = new VulnerabilityRelationRepository(db); } diff --git a/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityRelationRepository.java b/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityRelationRepository.java index 8dd500856e..49b4f9bf15 100644 --- a/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityRelationRepository.java +++ b/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityRelationRepository.java @@ -9,8 +9,8 @@ */ package org.eclipse.sw360.vulnerabilities.db; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.couchdb.DatabaseRepository; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.ReleaseVulnerabilityRelation; import org.ektorp.support.View; @@ -48,7 +48,7 @@ public class VulnerabilityRelationRepository extends DatabaseRepository { " } " + "}"; - public VulnerabilityRepository(DatabaseConnector db) { + public VulnerabilityRepository(DatabaseConnectorCloudant db) { super(Vulnerability.class, db); initStandardDesignDocument(); diff --git a/backend/svc-common/src/main/java/org/eclipse/sw360/SW360ServiceContextListener.java b/backend/svc-common/src/main/java/org/eclipse/sw360/SW360ServiceContextListener.java index 6ae48415b3..dd33f1b0b0 100644 --- a/backend/svc-common/src/main/java/org/eclipse/sw360/SW360ServiceContextListener.java +++ b/backend/svc-common/src/main/java/org/eclipse/sw360/SW360ServiceContextListener.java @@ -10,8 +10,7 @@ package org.eclipse.sw360; -import org.eclipse.sw360.datahandler.couchdb.DatabaseInstanceTracker; -import org.ektorp.http.IdleConnectionMonitor; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceTrackerCloudant; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; @@ -26,7 +25,6 @@ public void contextInitialized(ServletContextEvent sce) { @Override public void contextDestroyed(ServletContextEvent sce) { - DatabaseInstanceTracker.destroy(); - IdleConnectionMonitor.shutdown(); + DatabaseInstanceTrackerCloudant.destroy(); } } diff --git a/backend/utils/src/main/java/org/eclipse/sw360/attachments/db/RemoteAttachmentDownloader.java b/backend/utils/src/main/java/org/eclipse/sw360/attachments/db/RemoteAttachmentDownloader.java index 60e58a991a..42c8aa1412 100644 --- a/backend/utils/src/main/java/org/eclipse/sw360/attachments/db/RemoteAttachmentDownloader.java +++ b/backend/utils/src/main/java/org/eclipse/sw360/attachments/db/RemoteAttachmentDownloader.java @@ -19,14 +19,13 @@ import org.eclipse.sw360.datahandler.thrift.SW360Exception; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; -import com.cloudant.client.api.CloudantClient; +import com.ibm.cloud.cloudant.v1.Cloudant; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.util.List; import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; import static org.apache.logging.log4j.LogManager.getLogger; import static org.eclipse.sw360.datahandler.common.CommonUtils.closeQuietly; @@ -45,9 +44,9 @@ public static void main(String[] args) throws MalformedURLException { retrieveRemoteAttachments(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_ATTACHMENTS, downloadTimeout); } - public static int retrieveRemoteAttachments(Supplier httpClient, String dbAttachments, Duration downloadTimeout) throws MalformedURLException { - AttachmentConnector attachmentConnector = new AttachmentConnector(httpClient, dbAttachments, downloadTimeout); - AttachmentContentRepository attachmentContentRepository = new AttachmentContentRepository(new DatabaseConnectorCloudant(httpClient, dbAttachments)); + public static int retrieveRemoteAttachments(Cloudant client, String dbAttachments, Duration downloadTimeout) throws MalformedURLException { + AttachmentConnector attachmentConnector = new AttachmentConnector(client, dbAttachments, downloadTimeout); + AttachmentContentRepository attachmentContentRepository = new AttachmentContentRepository(new DatabaseConnectorCloudant(client, dbAttachments)); List remoteAttachments = attachmentContentRepository.getOnlyRemoteAttachments(); log.info("we have {} remote attachments to retrieve", remoteAttachments.size()); diff --git a/backend/utils/src/test/java/org/eclipse/sw360/attachments/db/RemoteAttachmentDownloaderTest.java b/backend/utils/src/test/java/org/eclipse/sw360/attachments/db/RemoteAttachmentDownloaderTest.java index e31e4a081d..71845d4c17 100644 --- a/backend/utils/src/test/java/org/eclipse/sw360/attachments/db/RemoteAttachmentDownloaderTest.java +++ b/backend/utils/src/test/java/org/eclipse/sw360/attachments/db/RemoteAttachmentDownloaderTest.java @@ -12,10 +12,8 @@ import org.eclipse.sw360.datahandler.TestUtils; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.common.DatabaseSettings; import org.eclipse.sw360.datahandler.common.Duration; import org.eclipse.sw360.datahandler.couchdb.AttachmentConnector; -import org.eclipse.sw360.datahandler.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.db.AttachmentContentRepository; import org.eclipse.sw360.datahandler.thrift.SW360Exception; import org.eclipse.sw360.datahandler.thrift.Visibility; diff --git a/libraries/datahandler/pom.xml b/libraries/datahandler/pom.xml index 78d505d2d8..1c443b05ce 100644 --- a/libraries/datahandler/pom.xml +++ b/libraries/datahandler/pom.xml @@ -188,10 +188,6 @@ org.jetbrains annotations - - org.ektorp - org.ektorp - commons-io commons-io @@ -207,6 +203,11 @@ cloudant-client ${cloudant.version} + + com.ibm.cloud + cloudant + ${cloudantsdk.version} + com.fasterxml.jackson.core jackson-databind @@ -219,10 +220,6 @@ com.fasterxml.jackson.core jackson-annotations - - com.github.stephenc.findbugs - findbugs-annotations - org.apache.thrift libthrift diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java index 79e563e426..7b771bcd8e 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java @@ -1,5 +1,5 @@ /* - * Copyright Siemens AG, 2021. Part of the SW360 Portal Project. + * Copyright Siemens AG, 2021, 2024. Part of the SW360 Portal Project. * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -9,63 +9,51 @@ */ package org.eclipse.sw360.datahandler.cloudantclient; -import java.io.IOException; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.gson.Gson; +import com.ibm.cloud.cloudant.v1.Cloudant; +import com.ibm.cloud.cloudant.v1.model.*; +import com.ibm.cloud.sdk.core.service.exception.ServiceResponseException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.thrift.TBase; +import org.apache.thrift.TFieldIdEnum; +import org.eclipse.sw360.datahandler.common.CommonUtils; +import org.eclipse.sw360.datahandler.thrift.ThriftUtils; +import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; + import java.io.InputStream; import java.lang.reflect.Field; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TreeSet; -import java.util.ArrayList; -import java.util.function.Supplier; import java.util.stream.Collectors; -import org.apache.http.HttpStatus; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.thrift.TBase; -import org.apache.thrift.TFieldIdEnum; -import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.eclipse.sw360.datahandler.thrift.ThriftUtils; -import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; - -import com.cloudant.client.api.CloudantClient; -import com.cloudant.client.api.Database; -import com.cloudant.client.api.DesignDocumentManager; -import com.cloudant.client.api.model.Response; -import com.cloudant.client.api.query.QueryResult; -import com.cloudant.client.api.views.Key; -import com.cloudant.client.api.views.ViewRequest; -import com.cloudant.client.api.views.ViewRequestBuilder; -import com.cloudant.client.api.views.ViewResponse; -import com.cloudant.client.api.views.ViewResponse.Row; -import com.cloudant.client.org.lightcouch.NoDocumentException; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - /** * Database Connector to a CouchDB database - * */ public class DatabaseConnectorCloudant { - + private final Logger log = LogManager.getLogger(DatabaseConnectorCloudant.class); private static final ImmutableList entitiesWithNonMatchingStructType = ImmutableList .of("moderation", "attachment", "usedReleaseRelation"); private final String dbName; private final DatabaseInstanceCloudant instance; - private final Database database; - public DatabaseConnectorCloudant(Supplier client, String dbName) { + public DatabaseConnectorCloudant(Cloudant client, String dbName) { this.instance = new DatabaseInstanceCloudant(client); this.dbName = dbName; - // Create the database if it does not exists yet - this.database = instance.createDB(dbName); + // Create the database if it does not exist yet + instance.createDB(dbName); } public String getDbName() { @@ -73,21 +61,16 @@ public String getDbName() { } public void update(Object document) { - Response resp; + DocumentResult resp; if (document != null) { final Class documentClass = document.getClass(); if (ThriftUtils.isMapped(documentClass)) { AttachmentContent content = (AttachmentContent) document; - try { - InputStream in = getAttachment(content.getId(), content.getFilename()); - resp = database.update(document); - createAttachment(resp.getId(), content.getFilename(), in, content.getContentType()); - } catch (NoDocumentException e) { - resp = database.update(document); - log.debug("No attachment associated with the document. Updating attachment content non metadata only"); - } + InputStream in = getAttachment(content.getId(), content.getFilename()); + resp = this.updateWithResponse(document); + createAttachment(resp.getId(), content.getFilename(), in, content.getContentType()); } else { - resp = database.update(document); + resp = this.updateWithResponse(document); } if (TBase.class.isAssignableFrom(document.getClass())) { TBase tbase = (TBase) document; @@ -101,10 +84,17 @@ public void update(Object document) { } } - public Response updateWithResponse(Object document) { - Response resp = null; + public DocumentResult updateWithResponse(Object document) { + DocumentResult resp = null; + Document doc = getDocumentFromPojo(document); if (document != null) { - resp = database.update(document); + PutDocumentOptions putDocumentOption = new PutDocumentOptions.Builder() + .db(this.dbName) + .docId(doc.getId()) + .document(doc) + .build(); + + resp = this.instance.getClient().putDocument(putDocumentOption).execute().getResult(); } return resp; } @@ -116,13 +106,19 @@ public DatabaseInstanceCloudant getInstance() { public Set getAllIds(Class type) { Set ids = Sets.newHashSet(); try { - List> rows = database.getViewRequestBuilder(type.getSimpleName(), "all") - .newRequest(Key.Type.STRING, Object.class).build().getResponse().getRows(); - for (Row r : rows) { + PostViewOptions viewOptions = new PostViewOptions.Builder() + .db(this.dbName) + .ddoc(type.getSimpleName()) + .view("all") + .build(); + + ViewResult response = this.instance.getClient().postView(viewOptions).execute().getResult(); + List rows = response.getRows(); + for (ViewResultRow r : rows) { String id = r.getId(); ids.add(id); } - } catch (IOException e) { + } catch (ServiceResponseException e) { log.error("Error fetching ids", e); } return ids; @@ -130,7 +126,13 @@ public Set getAllIds(Class type) { public T get(Class type, String id) { try { - T obj = (T) database.find(type, id); + GetDocumentOptions documentOption = new GetDocumentOptions.Builder() + .db(this.dbName) + .docId(id) + .build(); + Document doc = this.instance.getClient().getDocument(documentOption).execute().getResult(); + T obj = this.getPojoFromDocument(doc, type); + String extractedType = null; Field[] f = obj.getClass().getDeclaredFields(); for (Field fi : f) { @@ -158,8 +160,17 @@ public T get(Class type, String id) { public List getAll(Class type) { List list = Lists.newArrayList(); try { - list = database.getViewRequestBuilder(type.getSimpleName(), "all").newRequest(Key.Type.STRING, Object.class) - .includeDocs(true).build().getResponse().getDocsAs(type); + PostViewOptions viewOptions = new PostViewOptions.Builder() + .db(this.dbName) + .ddoc(type.getSimpleName()) + .view("all") + .includeDocs(true) + .build(); + + ViewResult response = this.instance.getClient().postView(viewOptions).execute().getResult(); + List rows = response.getRows(); + + list = rows.stream().map(r -> this.getPojoFromDocument(r.getDoc(), type)).collect(Collectors.toList()); } catch (Exception e) { log.error("Error getting documents", e); } @@ -167,27 +178,74 @@ public List getAll(Class type) { } public boolean remove(String id) { - Response resp = database.remove(id); - boolean success = resp.getStatusCode() == HttpStatus.SC_OK ? true : false; + DeleteDocumentOptions deleteOption = new DeleteDocumentOptions.Builder() + .db(this.dbName) + .docId(id) + .build(); + + DocumentResult resp = this.instance.getClient().deleteDocument(deleteOption).execute().getResult(); + boolean success = resp.isOk(); if (!success) { log.error("Could not delete document with id: " + id); } return success; } + public List getDocuments(Collection ids) { + if (!CommonUtils.isNotEmpty(ids)) + return Collections.emptyList(); + try { + Set idSet = new HashSet<>(ids); + + PostAllDocsOptions postDocsOption = new PostAllDocsOptions.Builder() + .db(this.dbName) + .keys(idSet.stream().toList()) + .includeDocs(true) + .build(); + + AllDocsResult resp = this.instance.getClient().postAllDocs(postDocsOption).execute().getResult(); + + return resp.getRows().stream() + .map(DocsResultRow::getDoc) + .filter(Objects::nonNull).collect(Collectors.toList()); + } catch (ServiceResponseException e) { + log.error("Error fetching documents", e); + return Collections.emptyList(); + } + } + + public Document getDocument(String id) { + try { + GetDocumentOptions getDocOption = new GetDocumentOptions.Builder() + .db(this.dbName) + .docId(id) + .build(); + + return this.instance.getClient().getDocument(getDocOption).execute().getResult(); + } catch (ServiceResponseException e) { + log.error("Error fetching document", e); + return new Document(); + } + } + public List get(Class type, Collection ids) { if (!CommonUtils.isNotEmpty(ids)) return Collections.emptyList(); try { Set idSet = new HashSet<>(ids); - String[] keys = new String[idSet.size()]; - int index = 0; - for (String str : idSet) - keys[index++] = str; - List docs = database.getAllDocsRequestBuilder().includeDocs(true).keys(keys).build().getResponse() - .getDocsAs(type); - return docs.stream().filter(Objects::nonNull).collect(Collectors.toList()); - } catch (IOException e) { + + PostAllDocsOptions postDocsOption = new PostAllDocsOptions.Builder() + .db(this.dbName) + .keys(idSet.stream().toList()) + .includeDocs(true) + .build(); + + AllDocsResult resp = this.instance.getClient().postAllDocs(postDocsOption).execute().getResult(); + + return resp.getRows().stream().map( + r -> this.getPojoFromDocument(r.getDoc(), type) + ).filter(Objects::nonNull).collect(Collectors.toList()); + } catch (ServiceResponseException e) { log.error("Error fetching documents", e); return Collections.emptyList(); } @@ -197,14 +255,24 @@ public List get(Class type, Collection ids, boolean ignoreNotF return get(type, ids); } - public List executeBulk(Collection list) { - List responses = Lists.newArrayList(); - List entities = Lists.newArrayList(list); + public List executeBulk(Collection list) { + BulkDocs bulkDocs = new BulkDocs.Builder() + .docs(list.stream().map(this::getDocumentFromPojo).collect(Collectors.toList())) + .build(); + + PostBulkDocsOptions bulkDocsOptions = new PostBulkDocsOptions.Builder() + .db(this.dbName) + .bulkDocs(bulkDocs) + .build(); + + List responses = Lists.newArrayList(); + + Object[] entities = list.toArray(); try { - responses = database.bulk(entities); - for (int i = 0; i < entities.size(); i++) { - if (TBase.class.isAssignableFrom(entities.get(i).getClass())) { - TBase tbase = (TBase) entities.get(i); + responses = this.instance.getClient().postBulkDocs(bulkDocsOptions).execute().getResult(); + for (int i = 0; i < entities.length; i++) { + if (TBase.class.isAssignableFrom(entities[i].getClass())) { + TBase tbase = (TBase) entities[i]; TFieldIdEnum id = tbase.fieldForId(1); TFieldIdEnum rev = tbase.fieldForId(2); tbase.setFieldValue(id, responses.get(i).getId()); @@ -218,34 +286,64 @@ public List executeBulk(Collection list) { return responses; } - public List deleteBulk(Collection deletionCandidates) { - return deletionCandidates.stream().map(x -> database.remove(x)).collect(Collectors.toList()); + public List deleteBulk(Collection deletionCandidates) { + BulkDocs bulkDocs = new BulkDocs.Builder() + .docs( + deletionCandidates.stream() + .peek(d -> d.setDeleted(true)) + .collect(Collectors.toList())) + .build(); + + PostBulkDocsOptions bulkDocsOptions = new PostBulkDocsOptions.Builder() + .db(this.dbName) + .bulkDocs(bulkDocs) + .build(); + + return this.instance.getClient().postBulkDocs(bulkDocsOptions).execute().getResult(); } - public List deleteIds(Class type, Collection ids) { - final List deletionCandidates = get(type, ids); + public List deleteIds(Collection ids) { + final List deletionCandidates = getDocuments(ids); return deleteBulk(deletionCandidates); } public int getDocumentCount(Class type) { int count = 0; try { - count = database.getViewRequestBuilder(type.getSimpleName(), "all") - .newRequest(Key.Type.STRING, Object.class).includeDocs(false).limit(0).build().getResponse() - .getTotalRowCount().intValue(); - } catch (IOException e) { + PostViewOptions viewOption = new PostViewOptions.Builder() + .db(this.dbName) + .ddoc(type.getSimpleName()) + .view("all") + .includeDocs(false) + .limit(0) + .build(); + + count = this.instance.getClient().postView(viewOption).execute().getResult().getTotalRows().intValue(); + } catch (ServiceResponseException e) { log.error("Error getting document count", e); } return count; } - public int getDocumentCount(Class type, String viewName, String []keys) { + public int getDocumentCount(Class type, String viewName, String[] keys) { int count = 0; try { - ViewResponse response = database.getViewRequestBuilder(type.getSimpleName(), viewName) - .newRequest(Key.Type.STRING, Integer.class).keys(keys).includeDocs(false).group(true).reduce(true).build().getResponse(); - count = response.getValues().stream().mapToInt(x->x).sum(); - } catch (IOException e) { + PostViewOptions viewOption = new PostViewOptions.Builder() + .db(this.dbName) + .ddoc(type.getSimpleName()) + .view(viewName) + .keys(Arrays.asList(keys)) + .includeDocs(false) + .group(true) + .reduce(true) + .build(); + + ViewResult resp = this.instance.getClient().postView(viewOption).execute().getResult(); + + count = resp.getRows().stream() + .mapToInt(r -> Integer.parseInt(r.getValue().toString())) + .sum(); + } catch (ServiceResponseException e) { log.error("Error getting document count", e); } return count; @@ -256,26 +354,61 @@ public ViewRequestBuilder createQuery(Class type, String queryName) { } public InputStream getAttachment(String docId, String attachmentName) { - return database.getAttachment(docId, attachmentName); + GetAttachmentOptions attachmentOptions = + new GetAttachmentOptions.Builder() + .db(this.dbName) + .docId(docId) + .attachmentName(attachmentName) + .build(); + + return this.instance.getClient().getAttachment(attachmentOptions).execute() + .getResult(); } - public void createAttachment(String attachmentContentId, String fileName, InputStream attachmentInputStream, - String contentType) { - String revision = database.find(AttachmentContent.class, attachmentContentId).getRevision(); - database.saveAttachment(attachmentInputStream, fileName, contentType, attachmentContentId, revision); + public void createAttachment(String attachmentContentId, String fileName, + InputStream attachmentInputStream, String contentType) { + GetDocumentOptions documentOption = new GetDocumentOptions.Builder() + .db(this.dbName) + .docId(attachmentContentId) + .build(); + Document doc = this.instance.getClient().getDocument(documentOption).execute().getResult(); + String revision = doc.getRev(); + + PutAttachmentOptions attachmentOptions = + new PutAttachmentOptions.Builder() + .db(this.dbName) + .docId(attachmentContentId) + .attachmentName(fileName) + .attachment(attachmentInputStream) + .contentType(contentType) + .rev(revision) + .build(); + + this.instance.getClient().putAttachment(attachmentOptions).execute() + .getResult(); } - public boolean deleteById(Class type, String id) { - Response result = null; - if (database.contains(id)) { - T obj = get(type, id); - result = database.remove(obj); + public boolean deleteById(String id) { + if (this.contains(id)) { + Document doc = getDocument(id); + + DeleteDocumentOptions deleteOption = new DeleteDocumentOptions.Builder() + .db(this.dbName) + .docId(id) + .rev(doc.getRev()) + .build(); + return this.instance.getClient().deleteDocument(deleteOption).execute().getResult().isOk(); } - return result.getStatusCode() == HttpStatus.SC_OK ? true : false; + return true; } public boolean add(T doc) { - Response resp = database.save(doc); + PostDocumentOptions postDocOption = new PostDocumentOptions.Builder() + .db(this.dbName) + .document(this.getDocumentFromPojo(doc)) + .build(); + + DocumentResult resp = this.instance.getClient().postDocument(postDocOption).execute().getResult(); if (TBase.class.isAssignableFrom(doc.getClass())) { TBase tbase = (TBase) doc; TFieldIdEnum id = tbase.fieldForId(1); @@ -283,20 +416,47 @@ public boolean add(T doc) { tbase.setFieldValue(id, resp.getId()); tbase.setFieldValue(rev, resp.getRev()); } - return resp.getStatusCode() == HttpStatus.SC_CREATED ? true : false; + return resp.isOk(); } public boolean remove(T doc) { - Response result = database.remove(doc); - return result.getStatusCode() == HttpStatus.SC_OK ? true : false; + return this.remove(this.getDocumentFromPojo(doc).getId()); } - public DesignDocumentManager getDesignDocumentManager() { - return database.getDesignDocumentManager(); + public boolean putDesignDocument(DesignDocument designDocument, String docId) { + PutDesignDocumentOptions designDocumentOptions = + new PutDesignDocumentOptions.Builder() + .db(this.dbName) + .designDocument(designDocument) + .ddoc(docId) + .build(); + + DocumentResult response = + this.instance.getClient() + .putDesignDocument(designDocumentOptions).execute() + .getResult(); + boolean success = response.isOk(); + if (!success) { + log.error("Unable to put design document {} to {}. Error: {}", + designDocument.getId(), docId, response.getError()); + } + return success; } - public void createIndex(String indexDefinition) { - database.createIndex(indexDefinition); + public void createIndex(IndexDefinition indexDefinition, String indexName, + String indexType) { + PostIndexOptions indexOptions = new PostIndexOptions.Builder() + .db(this.dbName) + .index(indexDefinition) + .name(indexName) + .type(indexType) + .build(); + + try { + this.instance.getClient().postIndex(indexOptions).execute().getResult(); + } catch (ServiceResponseException e) { + log.error("Error creating index", e); + } } public QueryResult getQueryResult(String query, Class type) { @@ -304,12 +464,17 @@ public QueryResult getQueryResult(String query, Class type) { } public Set getDistinctSortedStringKeys(Class type, String viewName) { - ViewRequest countReq1 = database.getViewRequestBuilder(type.getSimpleName(), viewName) - .newRequest(Key.Type.STRING, String.class).includeDocs(false).build(); + PostViewOptions viewOptions = new PostViewOptions.Builder() + .db(this.dbName) + .ddoc(type.getSimpleName()) + .view(viewName) + .includeDocs(false) + .build(); try { - ViewResponse response = countReq1.getResponse(); - return new TreeSet(response.getKeys()); - } catch (IOException e) { + ViewResult response = this.instance.getClient().postView(viewOptions).execute().getResult(); + return response.getRows().stream() + .map(r -> r.getKey().toString()).collect(Collectors.toCollection(TreeSet::new)); + } catch (ServiceResponseException e) { log.error("Error in getting project groups", e); } return Collections.emptySet(); @@ -319,18 +484,42 @@ public List getDocsByListIds(Class type, Collection ids) { if (!CommonUtils.isNotEmpty(ids)) return Collections.emptyList(); try { - List idList = new ArrayList<>(ids); - String[] keys = idList.stream().toArray(String[]::new); - List docs = database.getAllDocsRequestBuilder().includeDocs(true).keys(keys).build().getResponse() - .getDocsAs(type); - return docs.stream().filter(Objects::nonNull).collect(Collectors.toList()); - } catch (IOException e) { + PostAllDocsOptions postAllDocsOption = new PostAllDocsOptions.Builder() + .db(this.dbName) + .includeDocs(true) + .keys(ids.stream().toList()) + .build(); + + AllDocsResult resp = this.instance.getClient().postAllDocs(postAllDocsOption).execute().getResult(); + return resp.getRows().stream() + .map(r -> this.getPojoFromDocument(r.getDoc(), type)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } catch (ServiceResponseException e) { log.error("Error fetching documents", e); return Collections.emptyList(); } } - public Database getDatabase() { - return database; + public Document getDocumentFromPojo(Object document) { + Document doc = new Document(); + Gson gson = this.instance.getGson(); + doc.setProperties(gson.fromJson(gson.toJson(document), Map.class)); + return doc; + } + + public T getPojoFromDocument(Document document, Class type) { + return this.instance.getGson().fromJson(document.toString(), type); + } + + public boolean contains(String docId) { + HeadDocumentOptions documentOptions = + new HeadDocumentOptions.Builder() + .db(this.dbName) + .docId(docId) + .build(); + + return this.instance.getClient().headDocument(documentOptions).execute() + .getStatusCode() == 200; } } diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseInstanceCloudant.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseInstanceCloudant.java index 24d90042ae..bde4f6a480 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseInstanceCloudant.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseInstanceCloudant.java @@ -1,5 +1,5 @@ /* - * Copyright Siemens AG, 2021. Part of the SW360 Portal Project. + * Copyright Siemens AG, 2021, 2024. Part of the SW360 Portal Project. * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -9,10 +9,15 @@ */ package org.eclipse.sw360.datahandler.cloudantclient; -import java.util.function.Supplier; - -import com.cloudant.client.api.CloudantClient; -import com.cloudant.client.api.Database; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.ibm.cloud.cloudant.v1.Cloudant; +import com.ibm.cloud.cloudant.v1.model.DeleteDatabaseOptions; +import com.ibm.cloud.cloudant.v1.model.PutDatabaseOptions; +import com.ibm.cloud.sdk.core.service.exception.ServiceResponseException; +import org.eclipse.sw360.datahandler.common.CustomThriftDeserializer; +import org.eclipse.sw360.datahandler.common.CustomThriftSerializer; +import org.eclipse.sw360.datahandler.thrift.ThriftUtils; /** * Class for connecting to a given CouchDB instance @@ -23,29 +28,62 @@ public DatabaseInstanceCloudant() { DatabaseInstanceTrackerCloudant.track(this); } - CloudantClient client = null; + Cloudant client = null; + + private static Gson gson; - public DatabaseInstanceCloudant(Supplier client) { - this.client = client.get(); + public DatabaseInstanceCloudant(Cloudant client) { + this.client = client; } - public Database createDB(String dbName) { - return checkIfDbExists(dbName) ? client.database(dbName, false) : client.database(dbName, true); + public void createDB(String dbName) { + if (!checkIfDbExists(dbName)) { + PutDatabaseOptions putDbOptions = new PutDatabaseOptions.Builder().db(dbName).build(); + try { + client.putDatabase(putDbOptions).execute().getResult(); + } catch (ServiceResponseException e) { + if (e.getStatusCode() != 412) { + throw e; + } + } + } } public boolean checkIfDbExists(String dbName) { - return client.getAllDbs().contains(dbName); + return client.getAllDbs().execute().getResult().contains(dbName); } public void destroy() { - client.shutdown(); + client = null; } public void deleteDatabase(String dbName) { - client.deleteDB(dbName); + DeleteDatabaseOptions deleteDbOptions = new DeleteDatabaseOptions.Builder().db(dbName).build(); + try { + client.deleteDatabase(deleteDbOptions).execute().getResult(); + } catch (ServiceResponseException e) { + if (e.getStatusCode() != 404) { + throw e; + } + } } - public CloudantClient getClient() { + public Cloudant getClient() { return client; } + + public Gson getGson() { + if (gson == null) { + GsonBuilder gsonBuilder = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping(); + for (Class c : ThriftUtils.THRIFT_CLASSES) { + gsonBuilder.registerTypeAdapter(c, new CustomThriftDeserializer()); + gsonBuilder.registerTypeAdapter(c, new CustomThriftSerializer()); + } + for (Class c : ThriftUtils.THRIFT_NESTED_CLASSES) { + gsonBuilder.registerTypeAdapter(c, new CustomThriftSerializer()); + } + gson = gsonBuilder.create(); + } + return gson; + } } diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseRepositoryCloudantClient.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseRepositoryCloudantClient.java index 49eb250be4..3959a8df2c 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseRepositoryCloudantClient.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseRepositoryCloudantClient.java @@ -23,19 +23,11 @@ import org.eclipse.sw360.datahandler.thrift.Source; import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; -import com.cloudant.client.api.DesignDocumentManager; -import com.cloudant.client.api.model.DesignDocument; -import com.cloudant.client.api.model.DesignDocument.MapReduce; -import com.cloudant.client.api.query.JsonIndex; -import com.cloudant.client.api.model.Response; -import com.cloudant.client.api.views.Key; -import com.cloudant.client.api.views.Key.ComplexKey; -import com.cloudant.client.api.views.MultipleRequestBuilder; -import com.cloudant.client.api.views.UnpaginatedRequestBuilder; -import com.cloudant.client.api.views.ViewRequest; -import com.cloudant.client.api.views.ViewRequestBuilder; -import com.cloudant.client.api.views.ViewResponse; -import com.cloudant.client.org.lightcouch.NoDocumentException; +import com.ibm.cloud.cloudant.v1.model.DocumentResult; +import com.ibm.cloud.cloudant.v1.model.DesignDocument; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import com.ibm.cloud.cloudant.v1.model.IndexDefinition; +import com.ibm.cloud.cloudant.v1.model.IndexField; import com.google.common.collect.Lists; import com.google.gson.Gson; @@ -51,10 +43,12 @@ public class DatabaseRepositoryCloudantClient { private final Class type; private final DatabaseConnectorCloudant connector; - public void initStandardDesignDocument(Map views, DatabaseConnectorCloudant db) { - DesignDocument newDdoc = new DesignDocument(); + public void initStandardDesignDocument(Map views, + DatabaseConnectorCloudant db) { String ddocId = "_design/" + type.getSimpleName(); - newDdoc.setId(ddocId); + DesignDocument newDdoc = new DesignDocument.Builder() + .id(ddocId) + .build(); DesignDocument ddoc = db.get(DesignDocument.class, ddocId); if (ddoc == null) { db.add(newDdoc); @@ -62,22 +56,28 @@ public void initStandardDesignDocument(Map views, DatabaseCon DesignDocument ddocFinal = db.get(DesignDocument.class, ddocId); ddocFinal.setViews(views); db.update(ddocFinal); - DesignDocumentManager ddocManager = db.getDesignDocumentManager(); - ddocManager.put(ddocFinal); + db.putDesignDocument(ddocFinal, ddocId); } - public MapReduce createMapReduce(String map, String reduce) { - MapReduce mr = new MapReduce(); - mr.setMap(map); + public DesignDocumentViewsMapReduce createMapReduce(String map, String reduce) { + DesignDocumentViewsMapReduce.Builder mrBuilder = new DesignDocumentViewsMapReduce.Builder() + .map(map); if (reduce != null) { - mr.setReduce(reduce); + mrBuilder.reduce(reduce); } - return mr; + return mrBuilder.build(); } public void createIndex(String indexName, String[] fields, DatabaseConnectorCloudant db) { - String indexDefinition = JsonIndex.builder().name(indexName).desc(fields).definition(); - db.createIndex(indexDefinition); + IndexDefinition.Builder indexDefinitionBuilder = new IndexDefinition.Builder(); + for (String fieldName : fields) { + IndexField field = new IndexField.Builder() + .add(fieldName, "asc") + .build(); + indexDefinitionBuilder.addFields(field); + } + + db.createIndex(indexDefinitionBuilder.build(), indexName, "json"); } protected DatabaseConnectorCloudant getConnector() { @@ -370,7 +370,7 @@ public void update(T doc) { * This function should NOT be used for updating document containing Attachment. * Ex: Project, Component & Release */ - public Response updateWithResponse(T doc) { + public DocumentResult updateWithResponse(T doc) { return connector.updateWithResponse(doc); } @@ -391,7 +391,7 @@ public List getAll() { } public boolean remove(String id) { - return connector.deleteById(type, id); + return connector.deleteById(id); } public List get(Collection ids) { @@ -402,13 +402,12 @@ public List get(Collection ids, boolean ignoreNotFound) { return get(ids); } - public List executeBulk(Collection list) { + public List executeBulk(Collection list) { return connector.executeBulk(list); } - public List deleteIds(Collection ids) { - final List deletionCandidates = get(ids); - return connector.deleteBulk(deletionCandidates); + public List deleteIds(Collection ids) { + return connector.deleteIds(ids); } public int getDocumentCount() { diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/CommonUtils.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/CommonUtils.java index 74d3f38016..8148a352c2 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/CommonUtils.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/CommonUtils.java @@ -15,6 +15,7 @@ import com.google.common.base.*; import com.google.common.collect.*; +import com.ibm.cloud.cloudant.v1.model.DocumentResult; import org.eclipse.sw360.datahandler.thrift.*; import org.eclipse.sw360.datahandler.thrift.attachments.*; import org.eclipse.sw360.datahandler.thrift.components.Release; @@ -560,7 +561,7 @@ public static Map> getIdentifierToListOfDuplicates(ListMult } @NotNull - public static RequestSummary getRequestSummary(List ids, List documentOperationResults) { + public static RequestSummary getRequestSummary(List ids, List documentOperationResults) { final RequestSummary requestSummary = new RequestSummary(); requestSummary.requestStatus = documentOperationResults.isEmpty() ? RequestStatus.SUCCESS : RequestStatus.FAILURE; requestSummary.setTotalElements(ids.size()); diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/DatabaseSettings.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/DatabaseSettings.java index e7e1c3fba1..c600b1cc9b 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/DatabaseSettings.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/DatabaseSettings.java @@ -1,5 +1,5 @@ /* - * Copyright Siemens AG, 2014-2019. Part of the SW360 Portal Project. + * Copyright Siemens AG, 2014-2019,2024. Part of the SW360 Portal Project. * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -9,21 +9,16 @@ */ package org.eclipse.sw360.datahandler.common; -import java.net.MalformedURLException; -import java.net.URL; import java.util.Optional; import java.util.Properties; -import java.util.function.Supplier; +import com.ibm.cloud.sdk.core.security.Authenticator; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.eclipse.sw360.datahandler.thrift.ThriftUtils; -import org.ektorp.http.HttpClient; -import org.ektorp.http.StdHttpClient; -import com.cloudant.client.api.ClientBuilder; -import com.cloudant.client.api.CloudantClient; -import com.google.gson.GsonBuilder; +import com.ibm.cloud.cloudant.v1.Cloudant; +import com.ibm.cloud.cloudant.security.CouchDbSessionAuthenticator; +import org.jetbrains.annotations.NotNull; /** * Constants for the database address @@ -76,38 +71,22 @@ public class DatabaseSettings { LUCENE_LEADING_WILDCARD = Boolean.parseBoolean(props.getProperty("lucenesearch.leading.wildcard", "false")); } - public static Supplier getConfiguredHttpClient() throws MalformedURLException { - StdHttpClient.Builder httpClientBuilder = new StdHttpClient.Builder().url(COUCH_DB_URL); - if (!COUCH_DB_CACHE) { - httpClientBuilder.caching(false); - } + public static @NotNull Cloudant getConfiguredClient() { + Cloudant client; if (COUCH_DB_USERNAME.isPresent() && COUCH_DB_PASSWORD.isPresent()) { - httpClientBuilder.username(COUCH_DB_USERNAME.get()); - httpClientBuilder.password(COUCH_DB_PASSWORD.get()); - } - return httpClientBuilder::build; - } - - public static Supplier getConfiguredClient() { - ClientBuilder clientBuilder = null; - GsonBuilder gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping(); - for (Class c : ThriftUtils.THRIFT_CLASSES) { - gson.registerTypeAdapter(c, new CustomThriftDeserializer()); - gson.registerTypeAdapter(c, new CustomThriftSerializer()); - } - for (Class c : ThriftUtils.THRIFT_NESTED_CLASSES) { - gson.registerTypeAdapter(c, new CustomThriftSerializer()); + Authenticator authenticator = CouchDbSessionAuthenticator.newAuthenticator( + COUCH_DB_USERNAME.get(), + COUCH_DB_PASSWORD.get()); + client = new Cloudant("sw360-couchdb", authenticator); + } else { + client = Cloudant.newInstance("sw360-couchdb"); } try { - clientBuilder = ClientBuilder.url(new URL(COUCH_DB_URL)).gsonBuilder(gson); - if (COUCH_DB_USERNAME.isPresent() && COUCH_DB_PASSWORD.isPresent()) { - clientBuilder.username(COUCH_DB_USERNAME.get()); - clientBuilder.password(COUCH_DB_PASSWORD.get()); - } - } catch (MalformedURLException e) { - log.error("Error creating client", e); + client.setServiceUrl(COUCH_DB_URL); + } catch (IllegalArgumentException e) { + log.error("Error creating client: {}", e.getMessage(), e); } - return clientBuilder::build; + return client; } private DatabaseSettings() { diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentConnector.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentConnector.java index 50887f21da..405dbc1ec1 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentConnector.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentConnector.java @@ -9,8 +9,8 @@ */ package org.eclipse.sw360.datahandler.couchdb; -import com.cloudant.client.api.CloudantClient; import com.google.common.collect.Sets; +import com.ibm.cloud.cloudant.v1.Cloudant; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; @@ -24,7 +24,6 @@ import java.net.MalformedURLException; import java.util.Collection; import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; import static com.google.common.base.Strings.isNullOrEmpty; @@ -53,8 +52,8 @@ public AttachmentConnector(DatabaseConnectorCloudant databaseConnectorCloudant, /** * @todo remove this mess of constructors and use dependency injection */ - public AttachmentConnector(Supplier httpClient, String dbName, Duration downloadTimeout) throws MalformedURLException { - this(new DatabaseConnectorCloudant(httpClient, dbName), downloadTimeout); + public AttachmentConnector(Cloudant client, String dbName, Duration downloadTimeout) throws MalformedURLException { + this(new DatabaseConnectorCloudant(client, dbName), downloadTimeout); } /** @@ -74,7 +73,7 @@ public AttachmentContent getAttachmentContent(String attachmentContentId) throws } public void deleteAttachment(String id) { - connector.deleteById(AttachmentContent.class, id); + connector.deleteById(id); } public void deleteAttachments(Collection attachments) { @@ -83,7 +82,7 @@ public void deleteAttachments(Collection attachments) { } private void deleteAttachmentsByIds(Collection attachmentContentIds) { - connector.deleteIds(AttachmentContent.class, attachmentContentIds); + connector.deleteIds(attachmentContentIds); } public Set getAttachmentContentIds(Collection attachments) { diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentContentWrapper.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentContentWrapper.java deleted file mode 100644 index de4f961d5c..0000000000 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentContentWrapper.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Siemens AG, 2014-2015. Part of the SW360 Portal Project. - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ - -package org.eclipse.sw360.datahandler.couchdb; - -import com.fasterxml.jackson.annotation.JsonProperty; -import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; - -public class AttachmentContentWrapper extends DocumentWrapper { - - @JsonProperty("issetBitfield") - private byte __isset_bitfield = 0; //TODO set it - - /** - * must have a copy of all fields in @see Attachment - */ - public String id; // optional - public String revision; // optional - public String type; // optional - public boolean onlyRemote; // required - public String remoteUrl; // optional - public String filename; // required - public String contentType; // required - public String partsCount; // optional - - - @Override - public void updateNonMetadata(AttachmentContent source) { - filename = source.getFilename(); - type = source.getType(); - contentType = source.getContentType(); - remoteUrl = source.getRemoteUrl(); - partsCount = source.getPartsCount(); - remoteUrl = source.getRemoteUrl(); - onlyRemote = source.isOnlyRemote(); - } -} diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentStreamConnector.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentStreamConnector.java index 44bfd835da..204325f3ad 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentStreamConnector.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentStreamConnector.java @@ -23,7 +23,7 @@ import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; import org.eclipse.sw360.datahandler.thrift.users.User; -import com.cloudant.client.org.lightcouch.NoDocumentException; +import com.ibm.cloud.sdk.core.service.exception.ServiceResponseException; import java.io.IOException; import java.io.InputStream; @@ -217,7 +217,7 @@ public InputStream next() { part++; try { return connector.getAttachment(attachmentId, partFileName); - } catch (NoDocumentException e) { + } catch (ServiceResponseException e) { log.error("Cannot find part " + (part - 1) + " of attachment " + attachmentId, e); return null; } diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseConnector.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseConnector.java deleted file mode 100644 index 7d44b13892..0000000000 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseConnector.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright Siemens AG, 2014-2017, 2019. Part of the SW360 Portal Project. - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.sw360.datahandler.couchdb; - -import java.net.MalformedURLException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; -import java.util.function.Supplier; - -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.eclipse.sw360.datahandler.thrift.ThriftUtils; -import org.ektorp.BulkDeleteDocument; -import org.ektorp.DbAccessException; -import org.ektorp.DocumentNotFoundException; -import org.ektorp.DocumentOperationResult; -import org.ektorp.Security; -import org.ektorp.SecurityGroup; -import org.ektorp.Status; -import org.ektorp.UpdateConflictException; -import org.ektorp.ViewQuery; -import org.ektorp.ViewResult; -import org.ektorp.http.HttpClient; -import org.ektorp.impl.StdCouchDbConnector; -import org.ektorp.util.Documents; - -/** - * Database Connector to a CouchDB database - * - * @author cedric.bodet@tngtech.com - */ -public class DatabaseConnector extends StdCouchDbConnector { - - private static final Logger log = LogManager.getLogger(DatabaseConnector.class); - - private final String dbName; - private final DatabaseInstance instance; - - private String adminRole = "_admin"; - - /** - * Create a connection to the database - * - * @param httpClient HttpClient with authentication of CouchDB server - * @param dbName name of the database on the CouchDB server - */ - public DatabaseConnector(HttpClient httpClient, String dbName) throws MalformedURLException { - this(httpClient, dbName, new MapperFactory()); - } - - /** - * Create a connection to the database - * - * @param httpClient Supplier with authentication of CouchDB server - * @param dbName name of the database on the CouchDB server - */ - public DatabaseConnector(Supplier httpClient, String dbName) throws MalformedURLException { - this(httpClient.get(), dbName, new MapperFactory()); - } - - /** - * Create a connection to the database - * - * @param httpClient HttpClient with authentication of CouchDB server - * @param dbName name of the database on the CouchDB server - * @param mapperFactory Specific mapper factory to use for serialization - */ - public DatabaseConnector(HttpClient httpClient, String dbName, MapperFactory mapperFactory) throws MalformedURLException { - this(dbName, new DatabaseInstance(httpClient), mapperFactory); - } - - private DatabaseConnector(String dbName, DatabaseInstance instance, MapperFactory mapperFactory) throws MalformedURLException { - super(dbName, instance, mapperFactory); - this.instance = instance; - this.dbName = dbName; - // Create the database if it does not exists yet - instance.createDatabase(dbName); - restrictAccessToAdmins(); - } - - public Optional restrictAccessToAdmins() { - boolean hasChanged = false; - - Function addAdminRole = securityGroup -> { - List newGroupRoles = securityGroup.getRoles(); - newGroupRoles.add(adminRole); - return new SecurityGroup(securityGroup.getNames(), newGroupRoles); - }; - - Security security = Optional.ofNullable(getSecurity()) - .orElse(new Security()); - - SecurityGroup adminGroup = Optional.ofNullable(security.getAdmins()) - .orElse(new SecurityGroup()); - SecurityGroup memberGroup = Optional.ofNullable(security.getMembers()) - .orElse(new SecurityGroup()); - - if(!adminGroup.getRoles().contains(adminRole)){ - adminGroup = addAdminRole.apply(adminGroup); - hasChanged = true; - } - - if(!memberGroup.getRoles().contains(adminRole)){ - memberGroup = addAdminRole.apply(memberGroup); - hasChanged = true; - } - - if(hasChanged){ - return Optional.of(updateSecurity(new Security(adminGroup,memberGroup))); - } - return Optional.empty(); - } - - /** - * Creates the Object as a document in the database. If the id is not set it will be generated by the database. - */ - public boolean add(T document) { - try { - super.create(document); - return true; - } catch (UpdateConflictException e) { - log.warn("Update conflict exception while adding object!", e); - return false; - } catch (IllegalArgumentException e) { - log.warn("Illegal argument exception while adding document", e); - return false; - } - } - - /** - * Get an object of class type from the database and deserialize it. - */ - public T get(Class type, String id) { - try { - return super.get(type, id); - } catch (DocumentNotFoundException e) { - log.info("Document not found for ID: {}", id); - return null; - } catch (DbAccessException e) { - log.error("Document ID {} could not be successfully converted to {}", id, type.getName(), e); - return null; - } - } - - /** - * Get a list of documents from their IDs. All documents should be of the same type. - */ - public List get(Class type, Collection ids, boolean ignoreNotFound) { - if (ids == null) return Collections.emptyList(); - - // Copy to set in order to avoid duplicates - Set idSet = ImmutableSet.copyOf(ids); - - ViewQuery q = new ViewQuery() - .allDocs() - .includeDocs(true) - .keys(idSet); - q.setIgnoreNotFound(ignoreNotFound); - - List results = Lists.newArrayList(); - ViewResult result = queryView(q); - for (ViewResult.Row row : result.getRows()) { - String id = row.getId(); - results.add(get(type, id)); - } - return results; - } - - public List get(Class type, Collection ids) { - return get(type, ids, false); - } - - @Override - public void update(Object document) { - if (document != null) { - final Class documentClass = document.getClass(); - if (ThriftUtils.isMapped(documentClass)) { - DocumentWrapper wrapper = getDocumentWrapper(document, documentClass); - if (wrapper != null) { - super.update(wrapper); - } - } else { - super.update(document); - } - } else { - log.warn("Ignore updating a null document."); - } - } - - @SuppressWarnings("unchecked") - private DocumentWrapper getDocumentWrapper(Object document, Class documentClass) { - final Class wrapperClass = ThriftUtils.getWrapperClass(documentClass); - final String documentId = Documents.getId(document); - DocumentWrapper wrapper = get(wrapperClass, documentId); - - if (wrapper == null || !wrapper.getClass().equals(wrapperClass)) { - log.error("document {} cannot be wrapped", documentId); - return null; - } - - if (!wrapper.getId().equals(documentId)) { - log.error("round trip from database is not identity for id {}", documentId); - return null; - } - if (!wrapper.getRevision().equals(Documents.getRevision(document))) { - log.error("concurrent access to document {}", documentId); - return null; - } - - wrapper.updateNonMetadata(document); - return wrapper; - } - - /** - * Returns true if the database contains a document with the given ID. - */ - public boolean contains(String id) { - return (id != null) && super.contains(id); - } - - /** - * Delete the document with the given id. Returns true if the delete was successful. - */ - public boolean deleteById(String id) { - if (super.contains(id)) { - String rev = super.getCurrentRevision(id); - super.delete(id, rev); - return true; - } - return false; - } - - public String getDbName() { - return dbName; - } - - public DatabaseInstance getInstance() { - return instance; - } - - /** - * Deletes all objects in the supplied collection. - * - * @param deletionCandidates , the objects that will be deleted - * @return The list will only contain entries for documents that has any kind of error code returned from CouchDB. - * i.e. the list will be empty if everything was completed successfully. - */ - protected List deleteBulk(Collection deletionCandidates) { - List operations = new ArrayList<>(); - for (Object candidate : deletionCandidates) { - operations.add(BulkDeleteDocument.of(candidate)); - } - - return executeBulk(operations); - } - - public List deleteIds(Collection ids, Class type) { - final List deletionCandidates = get(type, ids); - return deleteBulk(deletionCandidates); - } -} diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseInstance.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseInstance.java deleted file mode 100644 index d156cb53fd..0000000000 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseInstance.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Siemens AG, 2014-2015. Part of the SW360 Portal Project. - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.sw360.datahandler.couchdb; - -import org.ektorp.http.HttpClient; -import org.ektorp.http.StdHttpClient; -import org.ektorp.impl.StdCouchDbInstance; - -import java.net.MalformedURLException; - -/** - * Class for connecting to a given CouchDB instance - */ -public class DatabaseInstance extends StdCouchDbInstance { - - /** - * Builds a CouchDB instance using ektorp - * - * @param httpClient HttpClient with authentication of the CouchDB instance - * @throws MalformedURLException - */ - public DatabaseInstance(HttpClient httpClient) throws MalformedURLException { - super(httpClient); - DatabaseInstanceTracker.track(this); - } - - @Override - public void createDatabase(String dbName) { - if (!checkIfDbExists(dbName)) { - super.createDatabase(dbName); - } - } - - public void destroy() { - getConnection().shutdown(); - } -} diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseInstanceTracker.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseInstanceTracker.java deleted file mode 100644 index d750e521da..0000000000 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseInstanceTracker.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Siemens AG, 2014-2015. Part of the SW360 Portal Project. - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ - -package org.eclipse.sw360.datahandler.couchdb; - -import java.util.concurrent.ConcurrentLinkedQueue; - -/** - * @author daniele.fognini@tngtech.com - */ -public class DatabaseInstanceTracker { - private static ConcurrentLinkedQueue trackedInstances = new ConcurrentLinkedQueue<>(); - - public static void track(DatabaseInstance databaseInstance) { - trackedInstances.add(databaseInstance); - } - - public static void destroy() { - DatabaseInstance trackedInstance; - while ((trackedInstance = trackedInstances.poll()) != null) { - trackedInstance.destroy(); - } - } -} diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseRepository.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseRepository.java deleted file mode 100644 index 468c6926ee..0000000000 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseRepository.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright Siemens AG, 2014-2018. Part of the SW360 Portal Project. - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.sw360.datahandler.couchdb; - -import org.ektorp.*; -import org.ektorp.support.CouchDbRepositorySupport; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * Access the database in a CRUD manner, for a generic class - * - * @author cedric.bodet@tngtech.com - * @author stefan.jaeger@evosoft.com - */ -public class DatabaseRepository extends CouchDbRepositorySupport { - - private static final char HIGH_VALUE_UNICODE_CHARACTER = '\uFFF0'; - - private final Class type; - private final DatabaseConnector connector; - - protected DatabaseConnector getConnector() { - return connector; - } - - public static Set getIds(ViewResult rows) { - HashSet ids = new HashSet<>(); - - for (ViewResult.Row row : rows.getRows()) { - ids.add(row.getId()); - } - return ids; - } - - private static List getIdList(ViewResult rows) { - return rows.getRows() - .stream() - .map(ViewResult.Row::getId) - .collect(Collectors.toList()); - } - - public static Set getStringValue(ViewResult rows) { - HashSet ids = new HashSet<>(); - - for (ViewResult.Row row : rows.getRows()) { - ids.add(row.getValue()); - } - return ids; - } - - public DatabaseRepository(Class type, DatabaseConnector databaseConnector) { - super(type, databaseConnector); - - this.connector = databaseConnector; - this.type = type; - } - - public Set queryForIds(ViewQuery query) { - ViewResult rows = connector.queryView(query.includeDocs(false)); - return getIds(rows); - } - - public List queryForIdList(ViewQuery query) { - ViewResult rows = connector.queryView(query.includeDocs(false)); - return getIdList(rows); - } - - public Set queryForIdsAsValue(ViewQuery query) { - ViewResult rows = connector.queryView(query.includeDocs(false)); - return getStringValue(rows); - } - - - public Set queryForIds(String queryName, String key) { - ViewQuery query = createQuery(queryName).key(key); - return queryForIds(query); - } - - - public Set queryForIds(String queryName, Set keys) { - ViewQuery query = createQuery(queryName).keys(keys); - return queryForIds(query); - } - - public Set queryForIdsAsValue(String queryName, String key) { - ViewQuery query = createQuery(queryName).key(key); - return queryForIdsAsValue(query); - } - - public Set queryForIdsAsComplexValue(String queryName, String... keys) { - ViewQuery query = createQuery(queryName).key(ComplexKey.of(keys)); - return queryForIds(query); - } - - public Set queryForIdsAsComplexValues(String queryName, Map> keys) { - Set complexKeys = keys.entrySet().stream() - .map(DatabaseRepository::createComplexKeys) - .flatMap(Collection::stream).collect(Collectors.toSet()); - ViewQuery query = createQuery(queryName).keys(complexKeys); - return queryForIds(query); - } - - public Set queryForIdsOnlyComplexKey(String queryName, String key) { - return queryForIdsOnlyComplexKeys(queryName, Collections.singleton(key)); - } - - public Set queryForIdsOnlyComplexKeys(String queryName, Set keys) { - Set queryResult = new HashSet<>(); - for (String key : keys) { - // If there is no value for the key just search for the key occurrence - // \ufff0 is used to ignore all other complex keys - ComplexKey startKeys = ComplexKey.of(key); - ComplexKey endKeys = ComplexKey.of(key, "\ufff0"); - queryResult.addAll(queryForIds(queryName, startKeys, endKeys)); - } - return queryResult; - } - - private static Set createComplexKeys(Map.Entry> key) { - return key.getValue().stream().map(v -> ComplexKey.of(key.getKey(), v)).collect(Collectors.toSet()); - } - - public Set queryForIdsAsValue(String queryName, Set keys) { - ViewQuery query = createQuery(queryName).keys(keys); - return queryForIdsAsValue(query); - } - - public Set getAllIdsByView(String queryName, boolean descending) { - ViewQuery query = createQuery(queryName).descending(descending); - return queryForIds(query); - } - - public Set queryForIds(String queryName, String startKey, String endKey) { - ViewQuery query = createQuery(queryName).startKey(startKey).endKey(endKey); - return queryForIds(query); - } - - public Set queryForIds(String queryName, ComplexKey startKey, ComplexKey endKey) { - ViewQuery query = createQuery(queryName).startKey(startKey).endKey(endKey); - return queryForIds(query); - } - - public List getIdListByView(String queryName, boolean descending, int limit) { - ViewQuery query = createQuery(queryName).descending(descending).limit(limit); - return queryForIdList(query); - } - - public Set getAllIds() { - ViewQuery query = createQuery("all"); - return queryForIds(query); - } - - public List queryByIds(String viewName, Collection ids) { - ViewQuery query = createQuery(viewName).includeDocs(true).keys(ids); - return queryView(query); - } - - public List queryByPrefix(String viewName, String key) { - return queryView(viewName, key, key + HIGH_VALUE_UNICODE_CHARACTER); - } - - public Set queryForIdsByPrefix(String viewName, String prefix) { - return queryForIds(viewName, prefix, prefix + HIGH_VALUE_UNICODE_CHARACTER); - } - - - public List queryView(String viewName, String startKey, String endKey) { - ViewQuery query = createQuery(viewName).startKey(startKey).endKey(endKey).includeDocs(true); - return queryView(query); - } - - public List queryView(ViewQuery query) { - return db.queryView(query, type); - } - - @Override - public T get(String id) { - try { - return super.get(id); - } catch (DocumentNotFoundException e) { - log.error("Document ID " + id + " not found!", e); - return null; - } catch (DbAccessException e) { - log.error("Document ID " + id + " could not be successfully converted.", e); - return null; - } - } - - - @Override - public List getAll() { - try { - return super.getAll(); - } catch (DocumentNotFoundException e) { - log.error("Nothing found!", e); - return null; - } catch (DbAccessException e) { - log.error("Documents could not be successfully converted.", e); - return null; - } catch (Exception e) { - log.error("Problem in getAll", e); - return null; - } - } - - public boolean remove(String id) { - return connector.deleteById(id); - } - - public List get(Collection ids) { - return connector.get(type, ids); - } - - public List get(Collection ids, boolean ignoreNotFound) { - return connector.get(type, ids, ignoreNotFound); - } - - - /** - * Creates, updates all objects in the supplied collection. - *

- * If the object has no revision set, it will be created, otherwise it will be updated. - *

- * Some documents may successfully be saved and some may not. The response will tell the application which documents - * were saved or not. In the case of a power failure, when the database restarts some may have been saved and some - * not. - * - * @param list , all objects will have their id and revision set. - * @return The list will only contain entries for documents that has any kind of error code returned from CouchDB. - * i.e. the list will be empty if everything was completed successfully. - */ - public List executeBulk(Collection list) { - try { - return connector.executeBulk(list); - } catch (Exception e) { - log.error("Problem in executeBulk with " + list, e); - return null; - } - } - - /** - * Deletes all objects in the supplied collection. - * - * @param deletionCandidates , the objects that will be deleted - * @return The list will only contain entries for documents that has any kind of error code returned from CouchDB. - * i.e. the list will be empty if everything was completed successfully. - */ - protected List deleteBulk(Collection deletionCandidates) { - return connector.deleteBulk(deletionCandidates); - } - - public List deleteIds(Collection ids) { - final List deletionCandidates = get(ids); - return deleteBulk(deletionCandidates); - } - - /** - * Returns the total count of documents in the repository (given by the all view) - */ - public int getDocumentCount() { - ViewQuery query = createQuery("all").includeDocs(false).limit(0); - return db.queryView(query).getTotalRows(); - } -} diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DocumentWrapper.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DocumentWrapper.java deleted file mode 100644 index 25a0564bc4..0000000000 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DocumentWrapper.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Siemens AG, 2014-2015. Part of the SW360 Portal Project. - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ - -package org.eclipse.sw360.datahandler.couchdb; - -import org.ektorp.support.CouchDbDocument; - -public abstract class DocumentWrapper extends CouchDbDocument { - public abstract void updateNonMetadata(T source); -} diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/MapperFactory.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/MapperFactory.java deleted file mode 100644 index 1fbac0b614..0000000000 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/MapperFactory.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright Siemens AG, 2014-2017. Part of the SW360 Portal Project. - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.sw360.datahandler.couchdb; - -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import org.eclipse.sw360.datahandler.thrift.ThriftUtils; -import org.ektorp.CouchDbConnector; -import org.ektorp.impl.ObjectMapperFactory; -import org.ektorp.impl.jackson.EktorpJacksonModule; - -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Mapper factory to bridge between Thrift generated objects and CouchDB serialization requirements - * - * @author cedric.bodet@tngtech.com - */ -public class MapperFactory implements ObjectMapperFactory { - - private final List> classes; - private final List> nestedClasses; - private final Map, JsonDeserializer> customDeserializer; - - - /** - * Create a mapper factory with mix-in for the thrift generated classes (defined in ThriftUtils) - */ - public MapperFactory() { - this(ThriftUtils.THRIFT_CLASSES, ThriftUtils.THRIFT_NESTED_CLASSES, ThriftUtils.CUSTOM_DESERIALIZER); - } - - /** - * Create a mapper factory with mix-in for the specified classes - * - * @param classes List of classes to add mix-ins to in the object mapper - */ - public MapperFactory(List> classes, List> nestedClasses, Map, JsonDeserializer> customDeserializer) { - this.classes = classes; - this.nestedClasses = nestedClasses; - this.customDeserializer = customDeserializer; - } - - /** - * Creates an object mapper with the given personalization - * - * @return the personalized object mapper - */ - @Override - @SuppressWarnings("unchecked") - public ObjectMapper createObjectMapper() { - ObjectMapper mapper = new ObjectMapper(); - - // General settings - mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); // auto-detect all member fields - mapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE); // but only public getters - mapper.setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE); // and none of "is-setters" - - // Do not include null - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - - SimpleModule module = new SimpleModule(); - for (Entry, JsonDeserializer> entry : customDeserializer.entrySet()) { - module.addDeserializer((Class) entry.getKey(), entry.getValue()); - } - mapper.registerModule(module); - - // Classes mix-in - for (Class type : classes) { - mapper.addMixInAnnotations(type, DatabaseMixIn.class); - } - - // Nested classes mix-in - for (Class type : nestedClasses) { - mapper.addMixInAnnotations(type, DatabaseNestedMixIn.class); - } - - return mapper; - } - - @Override - public ObjectMapper createObjectMapper(CouchDbConnector connector) { - ObjectMapper mapper = createObjectMapper(); - mapper.registerModule(new EktorpJacksonModule(connector, mapper)); - return mapper; - } - -} diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java index 25184966c4..4ca2196e4d 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java @@ -9,11 +9,6 @@ */ package org.eclipse.sw360.datahandler.couchdb.lucene; -import com.cloudant.client.api.CloudantClient; -import com.cloudant.client.api.DesignDocumentManager; -import com.cloudant.client.api.model.Response; -import com.cloudant.client.org.lightcouch.NoDocumentException; -import com.cloudant.client.org.lightcouch.internal.CouchDbUtil; import com.google.common.base.Joiner; import com.google.gson.Gson; import org.apache.logging.log4j.LogManager; @@ -43,7 +38,6 @@ import java.util.Map; import java.util.Set; import java.util.function.Function; -import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -78,25 +72,20 @@ public class NouveauLuceneAwareDatabaseConnector extends LuceneAwareCouchDbConne * Constructor using a Database connector */ public NouveauLuceneAwareDatabaseConnector(@NotNull DatabaseConnectorCloudant db, - Supplier dbClient, String ddoc) throws IOException { - super(db.getDatabase(), dbClient, ddoc); + super(db.getInstance().getClient(), ddoc); setResultLimit(DatabaseSettings.LUCENE_SEARCH_LIMIT); this.connector = db; } public boolean addDesignDoc(@NotNull NouveauDesignDocument designDocument) { - DesignDocumentManager designDocumentManager = this.connector.getDesignDocumentManager(); - - NouveauDesignDocument documentFromDb; - try { - documentFromDb = this.getNouveauDesignDocument(designDocument.getId()); - } catch (NoDocumentException ex) { - return putNouveauDesignDocument(designDocument, designDocumentManager); + NouveauDesignDocument documentFromDb = this.getNouveauDesignDocument(designDocument.getId()); + if (documentFromDb == null) { + return putNouveauDesignDocument(designDocument, this.connector); } if (!designDocument.equals(documentFromDb)) { - designDocument.setRevision(documentFromDb.getRevision()); + designDocument.setRev(documentFromDb.getRev()); if (documentFromDb.getNouveau() != null) { // Add missing indexes from existing DDOC as to not overwrite them documentFromDb.getNouveau().asMap().forEach((key, value) -> { @@ -105,7 +94,7 @@ public boolean addDesignDoc(@NotNull NouveauDesignDocument designDocument) { } }); } - return putNouveauDesignDocument(designDocument, designDocumentManager); + return putNouveauDesignDocument(designDocument, this.connector); } return true; } @@ -302,14 +291,15 @@ private static String sanitizeQueryInput(String input) { } private NouveauDesignDocument getNouveauDesignDocument(String id) { - CouchDbUtil.assertNotEmpty(id, "id"); - return this.connector.getDatabase().find(NouveauDesignDocument.class, id); + if (id.isEmpty()) { + throw new IllegalArgumentException("id cannot be empty"); + } + return this.connector.get(NouveauDesignDocument.class, id); } private static boolean putNouveauDesignDocument(NouveauDesignDocument designDocument, - @NotNull DesignDocumentManager designDocumentManager) { - Response response = designDocumentManager.put(designDocument); - return response.getError() == null || response.getError().isEmpty(); + @NotNull DatabaseConnectorCloudant connector) { + return connector.putDesignDocument(designDocument, designDocument.getId()); } private static @NotNull String formatDateNouveauFormat(@NotNull String date) throws ParseException { diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java index 7f5909b9e8..ee14006202 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java @@ -14,8 +14,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; -import org.eclipse.sw360.datahandler.couchdb.AttachmentContentWrapper; -import org.eclipse.sw360.datahandler.couchdb.DocumentWrapper; import org.eclipse.sw360.datahandler.couchdb.deserializer.UsageDataDeserializer; import org.eclipse.sw360.datahandler.thrift.attachments.*; import org.eclipse.sw360.datahandler.thrift.changelogs.ChangeLogs; @@ -40,7 +38,7 @@ import org.apache.logging.log4j.Logger; import org.apache.thrift.TBase; import org.apache.thrift.TFieldIdEnum; -import org.ektorp.util.Documents; +import com.ibm.cloud.cloudant.v1.model.Document; import java.util.Collection; import java.util.List; @@ -126,10 +124,6 @@ public static boolean isMapped(Class clazz) { return THRIFT_WRAPPED.containsKey(clazz); } - public static Class> getWrapperClass(Class clazz) { - return THRIFT_WRAPPED.get(clazz); - } - public static , F extends TFieldIdEnum> void copyField(T src, T dest, F field) { if (src.isSet(field)) { dest.setFieldValue(field, src.getFieldValue(field)); @@ -154,7 +148,7 @@ public static , FS extends TFieldIdEnum, D extends TBase< } public static Map getIdMap(Collection in) { - return Maps.uniqueIndex(in, Documents::getId); + return Maps.uniqueIndex(in, Document::getId); } public static , F extends TFieldIdEnum> Function extractField(final F field) { diff --git a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/common/DatabaseSettingsTest.java b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/common/DatabaseSettingsTest.java index 8a335aff6a..a2c19fdf7a 100644 --- a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/common/DatabaseSettingsTest.java +++ b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/common/DatabaseSettingsTest.java @@ -9,20 +9,14 @@ */ package org.eclipse.sw360.datahandler.common; +import com.ibm.cloud.cloudant.security.CouchDbSessionAuthenticator; +import com.ibm.cloud.cloudant.v1.Cloudant; +import com.ibm.cloud.sdk.core.security.Authenticator; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.eclipse.sw360.datahandler.thrift.ThriftUtils; -import org.ektorp.http.HttpClient; -import org.ektorp.http.StdHttpClient; - -import com.cloudant.client.api.ClientBuilder; -import com.cloudant.client.api.CloudantClient; -import com.google.gson.GsonBuilder; import java.net.MalformedURLException; -import java.net.URL; import java.util.Properties; -import java.util.function.Supplier; /** * Constants for the database address @@ -57,39 +51,22 @@ public class DatabaseSettingsTest { COUCH_DB_CHANGELOGS = props.getProperty("couchdb.change_logs", "sw360_test_changelogs"); } - public static Supplier getConfiguredHttpClient() throws MalformedURLException { - StdHttpClient.Builder httpClientBuilder = new StdHttpClient.Builder().url(COUCH_DB_URL); - if(! "".equals(COUCH_DB_USERNAME)) { - httpClientBuilder.username(COUCH_DB_USERNAME); - } - if (! "".equals(COUCH_DB_PASSWORD)) { - httpClientBuilder.password(COUCH_DB_PASSWORD); - } - return httpClientBuilder::build; - } - - public static Supplier getConfiguredClient() { - ClientBuilder clientBuilder = null; - GsonBuilder gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping(); - for (Class c : ThriftUtils.THRIFT_CLASSES) { - gson.registerTypeAdapter(c, new CustomThriftDeserializer()); - gson.registerTypeAdapter(c, new CustomThriftSerializer()); - } - for (Class c : ThriftUtils.THRIFT_NESTED_CLASSES) { - gson.registerTypeAdapter(c, new CustomThriftSerializer()); + public static Cloudant getConfiguredClient() { + Cloudant client; + if (!"".equals(COUCH_DB_USERNAME) && !"".equals(COUCH_DB_PASSWORD)) { + Authenticator authenticator = CouchDbSessionAuthenticator.newAuthenticator( + COUCH_DB_USERNAME, + COUCH_DB_PASSWORD); + client = new Cloudant("sw360-couchdb-test", authenticator); + } else { + client = Cloudant.newInstance("sw360-couchdb-test"); } try { - clientBuilder = ClientBuilder.url(new URL(COUCH_DB_URL)).gsonBuilder(gson); - if (!"".equals(COUCH_DB_USERNAME)) { - clientBuilder.username(COUCH_DB_USERNAME); - } - if (!"".equals(COUCH_DB_PASSWORD)) { - clientBuilder.password(COUCH_DB_PASSWORD); - } - } catch (MalformedURLException e) { - log.error("Error creating client", e); + client.setServiceUrl(COUCH_DB_URL); + } catch (IllegalArgumentException e) { + log.error("Error creating client: {}", e.getMessage(), e); } - return clientBuilder::build; + return client; } diff --git a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentConnectorTest.java b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentConnectorTest.java index d990176241..99041b596e 100644 --- a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentConnectorTest.java +++ b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentConnectorTest.java @@ -68,7 +68,7 @@ public void testDeleteAttachmentsDifference() throws Exception { deletedIds.add("a2cid"); attachmentConnector.deleteAttachmentDifference(before, after); - verify(connector).deleteIds(AttachmentContent.class, deletedIds); + verify(connector).deleteIds(deletedIds); } @Test @@ -96,8 +96,6 @@ public void testDeleteAttachmentsDifferenceOnlyNonAcceptedIsDeleted() throws Exc expectedIdsToDelete.add("a2"); attachmentConnector.deleteAttachmentDifference(before, after); - verify(connector).deleteIds(AttachmentContent.class, expectedIdsToDelete); + verify(connector).deleteIds(expectedIdsToDelete); } - - } diff --git a/libraries/nouveau-handler/pom.xml b/libraries/nouveau-handler/pom.xml index c01a62a48b..7ac18044ec 100644 --- a/libraries/nouveau-handler/pom.xml +++ b/libraries/nouveau-handler/pom.xml @@ -169,6 +169,11 @@ cloudant-client ${cloudant.version} + + com.ibm.cloud + cloudant + ${cloudantsdk.version} + commons-lang commons-lang diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java index 39fb45982a..3dedc4797a 100644 --- a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java @@ -9,18 +9,25 @@ */ package org.eclipse.sw360.nouveau; -import com.cloudant.client.api.CloudantClient; -import com.cloudant.client.api.Database; import com.cloudant.client.internal.DatabaseURIHelper; -import com.cloudant.client.org.lightcouch.CouchDbException; -import com.cloudant.client.org.lightcouch.internal.CouchDbUtil; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import com.ibm.cloud.cloudant.common.SdkCommon; +import com.ibm.cloud.cloudant.v1.Cloudant; +import com.ibm.cloud.cloudant.v1.model.SearchAnalyzeResult; +import com.ibm.cloud.sdk.core.http.RequestBuilder; +import com.ibm.cloud.sdk.core.http.ResponseConverter; +import com.ibm.cloud.sdk.core.util.ResponseConverterUtils; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.io.InputStream; import java.net.URI; -import java.util.function.Supplier; +import java.util.Iterator; +import java.util.Map; /** * CouchDB connector which allows us to run the Nouveau queries. @@ -34,26 +41,47 @@ public class LuceneAwareCouchDbConnector { private final String ddoc; private final NouveauAwareDatabase database; - public LuceneAwareCouchDbConnector(Database db, Supplier dbClient, String ddoc) throws IOException { + public LuceneAwareCouchDbConnector(Cloudant db, String ddoc) throws IOException { this.lucenePrefix = DEFAULT_NOUVEAU_PREFIX; this.ddoc = ddoc; - this.database = new NouveauAwareDatabase(db, dbClient, this.ddoc, this.lucenePrefix); + this.database = new NouveauAwareDatabase(db, this.ddoc, this.lucenePrefix); } - public static class NouveauAwareDatabase extends Database { + public static class NouveauAwareDatabase { private final String ddoc; - private final CloudantClient client; + private final Cloudant client; private final String lucenePrefix; - protected NouveauAwareDatabase(Database db, @NotNull Supplier dbClient, + protected NouveauAwareDatabase(@NotNull Cloudant dbClient, String ddoc, String lucenePrefix) { - super(db); - this.client = dbClient.get(); + this.client = dbClient; this.ddoc = ddoc; this.lucenePrefix = lucenePrefix; } public T queryNouveau(String index, @NotNull NouveauQuery query, Class classOfT) { +// RequestBuilder builder = RequestBuilder.post( +// RequestBuilder.resolveRequestUrl( +// this.client.getServiceUrl(), +// "/" + ensureDesignId(this.ddoc) + "/" + lucenePrefix + "/" + index +// )); +// Map sdkHeaders = SdkCommon.getSdkHeaders( +// "cloudant", "v1", "postNouveauQuery"); +// Iterator> var4 = sdkHeaders.entrySet().iterator(); +// +// while (var4.hasNext()) { +// Map.Entry header = var4.next(); +// builder.header(new Object[]{header.getKey(), header.getValue()}); +// } +// +// builder.header(new Object[]{"Accept", "application/json"}); +// JsonObject contentJson = this.getGson().fromJson(query.buildQuery(this.getGson()), JsonObject.class); +// builder.bodyJson(contentJson); +// ResponseConverter responseConverter = ResponseConverterUtils.getValue((new TypeToken() { +// }).getType()); +// this.client.createServiceCall(builder.build(), responseConverter); + + URI uri = (new DatabaseURIHelper(this.getDBUri())) .path(ensureDesignId(this.ddoc)) .path(lucenePrefix).path(index).build(); @@ -72,6 +100,18 @@ public T queryNouveau(String index, @NotNull NouveauQuery query, Class cl return queryResponse; } + +// private Gson getGson() { +// GsonBuilder gsonBuilder = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping(); +// for (Class c : ThriftUtils.THRIFT_CLASSES) { +// gsonBuilder.registerTypeAdapter(c, new CustomThriftDeserializer()); +// gsonBuilder.registerTypeAdapter(c, new CustomThriftSerializer()); +// } +// for (Class c : ThriftUtils.THRIFT_NESTED_CLASSES) { +// gsonBuilder.registerTypeAdapter(c, new CustomThriftSerializer()); +// } +// return gsonBuilder.create(); +// } } /** diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauDesignDocument.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauDesignDocument.java index 86f9422fbc..39c866696d 100644 --- a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauDesignDocument.java +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauDesignDocument.java @@ -9,7 +9,8 @@ */ package org.eclipse.sw360.nouveau.designdocument; -import com.cloudant.client.api.model.DesignDocument; +import com.ibm.cloud.cloudant.v1.model.DesignDocument; +import com.google.gson.annotations.SerializedName; import com.google.gson.Gson; import com.google.gson.JsonObject; import org.jetbrains.annotations.NotNull; @@ -21,6 +22,7 @@ */ public class NouveauDesignDocument extends DesignDocument { + @SerializedName("nouveau") private JsonObject nouveau; public JsonObject getNouveau() { diff --git a/pom.xml b/pom.xml index 0b363cba1f..0a978126bd 100644 --- a/pom.xml +++ b/pom.xml @@ -91,6 +91,7 @@ 1.12.13 2.19.1 + 0.9.1 1.15 4.4 1.21 @@ -100,7 +101,6 @@ 3.12.0 1.10.0 2.1.3 - 1.5.0 1.0.1 1.3.9-1 2.3.9 @@ -299,17 +299,6 @@ commons-collections4 ${commons-collection4.version} - - - com.github.stephenc.findbugs - findbugs-annotations - ${findbugs-annotations.version} - - - org.ektorp - org.ektorp - ${ektorp.version} - commons-io commons-io @@ -407,6 +396,11 @@ cloudant-client ${cloudant.version} + + com.ibm.cloud + cloudant + ${cloudantsdk.version} + junit junit diff --git a/rest/authorization-server/pom.xml b/rest/authorization-server/pom.xml index 0662977c09..ea0f54dffe 100644 --- a/rest/authorization-server/pom.xml +++ b/rest/authorization-server/pom.xml @@ -144,17 +144,6 @@ org.springframework.security spring-security-core - - org.ektorp - org.ektorp - ${ektorp.version} - - - net.sourceforge.findbugs - annotations - - - From 3e7bdad76208c78b3a7223b76dccf7fc4c510191 Mon Sep 17 00:00:00 2001 From: Gaurav Mishra Date: Mon, 5 Aug 2024 16:46:29 +0530 Subject: [PATCH 06/10] fix(cloudant): fix views Signed-off-by: Gaurav Mishra --- .../db/AttachmentContentRepository.java | 10 +- .../db/AttachmentOwnerRepository.java | 13 +- .../datahandler/db/AttachmentRepository.java | 23 +- .../db/AttachmentUsageRepository.java | 105 +++---- .../sw360/datahandler/db/BulkDeleteUtil.java | 24 +- .../datahandler/db/ComponentRepository.java | 80 +++--- .../db/ConfigContainerRepository.java | 13 +- .../datahandler/db/PackageRepository.java | 59 ++-- .../datahandler/db/ProjectRepository.java | 35 +-- .../db/RelationsUsageRepository.java | 11 +- .../datahandler/db/ReleaseRepository.java | 99 +++---- .../sw360/datahandler/db/RepositoryUtils.java | 4 +- .../sw360/datahandler/db/UserRepository.java | 18 +- .../db/ModerationRequestRepository.java | 76 +++--- .../DatabaseConnectorCloudant.java | 39 ++- .../DatabaseRepositoryCloudantClient.java | 256 +++++++++--------- 16 files changed, 425 insertions(+), 440 deletions(-) diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentContentRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentContentRepository.java index 3204d21087..b077f14e1e 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentContentRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentContentRepository.java @@ -20,9 +20,7 @@ import com.ibm.cloud.cloudant.v1.model.DocumentResult; import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; -import com.cloudant.client.api.views.Key; -import com.cloudant.client.api.views.UnpaginatedRequestBuilder; -import com.cloudant.client.api.views.ViewRequestBuilder; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; import java.util.HashMap; import java.util.List; @@ -51,8 +49,10 @@ public AttachmentContentRepository(DatabaseConnectorCloudant db) { } public List getOnlyRemoteAttachments() { - ViewRequestBuilder query = getConnector().createQuery(AttachmentContent.class, "onlyRemotes"); - UnpaginatedRequestBuilder req = query.newRequest(Key.Type.STRING, Object.class).includeDocs(true); + PostViewOptions req = getConnector() + .getPostViewQueryBuilder(AttachmentContent.class, "onlyRemotes") + .includeDocs(true) + .build(); return queryView(req); } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentOwnerRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentOwnerRepository.java index 0b530e01fd..5c7f8aa0d5 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentOwnerRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentOwnerRepository.java @@ -14,7 +14,8 @@ import org.eclipse.sw360.datahandler.thrift.Source; import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; -import com.cloudant.client.api.views.ViewRequestBuilder; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; +import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.List; @@ -36,8 +37,12 @@ public AttachmentOwnerRepository(DatabaseConnectorCloudant db) { initStandardDesignDocument(views, db); } - public List getOwnersByIds(Set ids) { - ViewRequestBuilder viewQuery = getConnector().createQuery(Source.class, "attachmentOwner"); - return queryViewForSource(buildRequest(viewQuery, ids)); + public List getOwnersByIds(@NotNull Set ids) { + PostViewOptions viewQuery = getConnector() + .getPostViewQueryBuilder(Source.class, "attachmentOwner") + .includeDocs(false) + .keys(ids.stream().map(r -> (Object)r).toList()) + .build(); + return queryViewForSource(viewQuery); } } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentRepository.java index 2e91da77b1..a307f28b82 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentRepository.java @@ -14,7 +14,8 @@ import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; -import com.cloudant.client.api.views.ViewRequestBuilder; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; +import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.List; @@ -38,13 +39,21 @@ public AttachmentRepository(DatabaseConnectorCloudant db) { initStandardDesignDocument(views, db); } - public List getAttachmentsByIds(Set ids) { - ViewRequestBuilder viewQuery = getConnector().createQuery(Attachment.class, "byid"); - return queryViewForAttchmnt(buildRequest(viewQuery, ids)); + public List getAttachmentsByIds(@NotNull Set ids) { + PostViewOptions viewQuery = getConnector() + .getPostViewQueryBuilder(Attachment.class, "byid") + .includeDocs(false) + .keys(ids.stream().map(r -> (Object)r).toList()) + .build(); + return queryViewForAttachment(viewQuery); } - public List getAttachmentsBySha1s(Set sha1s) { - ViewRequestBuilder viewQuery = getConnector().createQuery(Attachment.class, "bysha1"); - return queryViewForAttchmnt(buildRequest(viewQuery, sha1s)); + public List getAttachmentsBySha1s(@NotNull Set sha1s) { + PostViewOptions viewQuery = getConnector() + .getPostViewQueryBuilder(Attachment.class, "bysha1") + .includeDocs(false) + .keys(sha1s.stream().map(r -> (Object)r).toList()) + .build(); + return queryViewForAttachment(viewQuery); } } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java index ccde27d9b5..7efdbf7f04 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java @@ -10,16 +10,13 @@ package org.eclipse.sw360.datahandler.db; -import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; -import com.cloudant.client.api.views.Key; -import com.cloudant.client.api.views.MultipleRequestBuilder; -import com.cloudant.client.api.views.UnpaginatedRequestBuilder; -import com.cloudant.client.api.views.ViewRequestBuilder; -import com.cloudant.client.api.views.ViewResponse; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; +import com.ibm.cloud.cloudant.v1.model.ViewResult; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentUsage; @@ -66,34 +63,53 @@ public AttachmentUsageRepository(DatabaseConnectorCloudant db) { } public List getUsageForAttachment(String ownerId, String attachmentContentId) { - ViewRequestBuilder viewQuery = getConnector().createQuery(AttachmentUsage.class, "usagesByAttachment"); - UnpaginatedRequestBuilder reqBuilder = viewQuery.newRequest(Key.Type.COMPLEX, Object.class).keys(Key.complex(new String[] {ownerId, attachmentContentId})).includeDocs(true).reduce(false); - return queryView(reqBuilder); + PostViewOptions viewQuery = getConnector() + .getPostViewQueryBuilder(AttachmentUsage.class, "usagesByAttachment") + .includeDocs(true) + .reduce(false) + .keys(List.of(new String[] { ownerId, attachmentContentId })) + .build(); + return queryView(viewQuery); } public List getUsedAttachments(String usedById) { - ViewRequestBuilder viewQuery = getConnector().createQuery(AttachmentUsage.class, "usedAttachments"); - UnpaginatedRequestBuilder reqBuilder = viewQuery.newRequest(Key.Type.STRING, Object.class).includeDocs(true).reduce(false).keys(usedById); - return queryView(reqBuilder); + PostViewOptions viewQuery = getConnector() + .getPostViewQueryBuilder(AttachmentUsage.class, "usedAttachments") + .includeDocs(true) + .reduce(false) + .keys(List.of(usedById)) + .build(); + return queryView(viewQuery); } public List getUsedAttachmentById(String attachmentContentId) { - ViewRequestBuilder viewQuery = getConnector().createQuery(AttachmentUsage.class, "usedAttachmentById"); - UnpaginatedRequestBuilder reqBuilder = viewQuery.newRequest(Key.Type.STRING, Object.class).includeDocs(true).reduce(false).keys(attachmentContentId); - return queryView(reqBuilder); + PostViewOptions viewQuery = getConnector() + .getPostViewQueryBuilder(AttachmentUsage.class, "usedAttachmentById") + .includeDocs(true) + .reduce(false) + .keys(List.of(attachmentContentId)) + .build(); + return queryView(viewQuery); } public List getUsageForAttachment(String ownerId, String attachmentContentId, String filter) { - ViewRequestBuilder viewQuery = getConnector().createQuery(AttachmentUsage.class, "usagesByAttachmentUsageType"); - UnpaginatedRequestBuilder reqBuilder = viewQuery.newRequest(Key.Type.COMPLEX, Object.class).includeDocs(true).reduce(false) - .keys(Key.complex(new String[] { ownerId, attachmentContentId, filter })); - return queryView(reqBuilder); + PostViewOptions viewQuery = getConnector() + .getPostViewQueryBuilder(AttachmentUsage.class, "usagesByAttachmentUsageType") + .includeDocs(true) + .reduce(false) + .keys(List.of(new String[] { ownerId, attachmentContentId, filter })) + .build(); + return queryView(viewQuery); } public List getUsedAttachments(String usedById, String filter) { - ViewRequestBuilder viewQuery = getConnector().createQuery(AttachmentUsage.class, "usedAttachmentsUsageType"); - UnpaginatedRequestBuilder reqBuilder = viewQuery.newRequest(Key.Type.COMPLEX, Object.class).includeDocs(true).reduce(false).keys(Key.complex(new String[] { usedById, filter })); - return queryView(reqBuilder); + PostViewOptions viewQuery = getConnector() + .getPostViewQueryBuilder(AttachmentUsage.class, "usedAttachmentsUsageType") + .includeDocs(true) + .reduce(false) + .keys(List.of(new String[] { usedById, filter })) + .build(); + return queryView(viewQuery); } public List getUsagesByReleaseId(String releaseId) { @@ -101,50 +117,39 @@ public List getUsagesByReleaseId(String releaseId) { } public Map, Integer> getAttachmentUsageCount(Map> attachments, String filter) { - ViewRequestBuilder viewQuery = createUsagesByAttachmentQuery(filter); - List complexKeysList = prepareKeys(attachments, filter); - Key.ComplexKey[] compexKeys = new Key.ComplexKey[complexKeysList.size()]; - for (int i = 0; i < compexKeys.length; i++) { - Key.ComplexKey key = Key.complex(complexKeysList.get(i)); - compexKeys[i] = key; - } - UnpaginatedRequestBuilder reqBuilder = viewQuery.newRequest(Key.Type.COMPLEX, Object.class).reduce(true).group(true).keys(compexKeys); - ViewResponse result = queryViewForComplexKeys(reqBuilder); + PostViewOptions.Builder viewQuery = createUsagesByAttachmentQuery(filter); + @NotNull List complexKeysList = prepareKeys(attachments, filter); + PostViewOptions req = viewQuery.reduce(true).group(true).keys(complexKeysList).build(); + ViewResult result = queryViewForComplexKeys(req); return result.getRows().stream().collect(Collectors.toMap(key -> { - String json = key.getKey().toJson(); - String replace = json.replace("[","").replace("]","").replaceAll("\"",""); - List relIdAttachmentToUsageType = new ArrayList(Arrays.asList(replace.split(","))); + String json = key.getKey().toString(); + String replace = json.replace("[", "").replace("]", "").replaceAll("\"", ""); + List relIdAttachmentToUsageType = new ArrayList<>(Arrays.asList(replace.split(","))); return ImmutableMap.of(relIdAttachmentToUsageType.get(0), relIdAttachmentToUsageType.get(1)); }, val -> ((Double) val.getValue()).intValue())); } - public List getUsageForAttachments(Map> attachments, String filter) { - ViewRequestBuilder viewQuery = createUsagesByAttachmentQuery(filter); - @NotNull List complexKeysList = prepareKeys(attachments, filter); - Key.ComplexKey[] compexKeys = new Key.ComplexKey[complexKeysList.size()]; - for (int i = 0; i < compexKeys.length; i++) { - Key.ComplexKey key = Key.complex(complexKeysList.get(i)); - compexKeys[i] = key; - } - MultipleRequestBuilder reqBuilder = viewQuery.newMultipleRequest(Key.Type.COMPLEX, Object.class).includeDocs(true).reduce(false).keys(compexKeys); - return multiRequestqueryView(reqBuilder); + PostViewOptions.Builder viewQuery = createUsagesByAttachmentQuery(filter); + @NotNull List complexKeysList = prepareKeys(attachments, filter); + PostViewOptions req = viewQuery.includeDocs(true).reduce(false).keys(complexKeysList).build(); + return queryView(req); } - private ViewRequestBuilder createUsagesByAttachmentQuery(String filter) { - ViewRequestBuilder viewQuery; + private PostViewOptions.Builder createUsagesByAttachmentQuery(String filter) { + PostViewOptions.Builder viewQuery; if (Strings.isNullOrEmpty(filter)) { - viewQuery = getConnector().createQuery(AttachmentUsage.class, "usagesByAttachment"); + viewQuery = getConnector().getPostViewQueryBuilder(AttachmentUsage.class, "usagesByAttachment"); } else { - viewQuery = getConnector().createQuery(AttachmentUsage.class, "usagesByAttachmentUsageType"); + viewQuery = getConnector().getPostViewQueryBuilder(AttachmentUsage.class, "usagesByAttachmentUsageType"); } return viewQuery; } @NotNull - private List prepareKeys(Map> attachments, String filter) { - List keys = Lists.newArrayList(); + private List prepareKeys(@NotNull Map> attachments, String filter) { + List keys = Lists.newArrayList(); for (Entry> entry : attachments.entrySet()) { for (String attachmentId : entry.getValue()) { if (Strings.isNullOrEmpty(filter)) { diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/BulkDeleteUtil.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/BulkDeleteUtil.java index edd89699e4..3887813356 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/BulkDeleteUtil.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/BulkDeleteUtil.java @@ -29,8 +29,8 @@ import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.permissions.PermissionUtils; -import com.cloudant.client.api.model.Document; -import com.cloudant.client.api.model.Response; +import com.ibm.cloud.cloudant.v1.model.Document; +import com.ibm.cloud.cloudant.v1.model.DocumentResult; import com.google.common.collect.Lists; import java.util.ArrayList; @@ -528,12 +528,12 @@ public Map deleteBulkRelease(List rel for(Release release : releaseList) { Document document = new Document(); document.setId(release.getId()); - document.setRevision(release.getRevision()); + document.setRev(release.getRevision()); document.setDeleted(true); documentList.add(document); } - List responseList = releaseRepository.executeBulk(documentList); - for (Response response : responseList) { + List responseList = releaseRepository.executeBulk(documentList); + for (DocumentResult response : responseList) { String documentId = response.getId(); String error = response.getError(); if (CommonUtils.isNullEmptyOrWhitespace(error)) { @@ -549,8 +549,8 @@ public Map deleteBulkRelease(List rel public Map updateBulkReleases(Collection collection) { Map resultState = new HashMap(); - List responseList = releaseRepository.executeBulk(collection); - for (Response response : responseList) { + List responseList = releaseRepository.executeBulk(collection); + for (DocumentResult response : responseList) { String documentId = response.getId(); String error = response.getError(); if (CommonUtils.isNullEmptyOrWhitespace(error)) { @@ -571,12 +571,12 @@ public Map deleteBulkComponent(List for(Component component : componentList) { Document document = new Document(); document.setId(component.getId()); - document.setRevision(component.getRevision()); + document.setRev(component.getRevision()); document.setDeleted(true); documentList.add(document); } - List responseList = releaseRepository.executeBulk(documentList); - for (Response response : responseList) { + List responseList = releaseRepository.executeBulk(documentList); + for (DocumentResult response : responseList) { String documentId = response.getId(); String error = response.getError(); if (CommonUtils.isNullEmptyOrWhitespace(error)) { @@ -592,8 +592,8 @@ public Map deleteBulkComponent(List public Map updateBulkComponent(Collection collection) { Map resultState = new HashMap(); - List responseList = componentRepository.executeBulk(collection); - for (Response response : responseList) { + List responseList = componentRepository.executeBulk(collection); + for (DocumentResult response : responseList) { String documentId = response.getId(); String error = response.getError(); if (CommonUtils.isNullEmptyOrWhitespace(error)) { diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentRepository.java index f526b3f8f4..ded82d1d4a 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentRepository.java @@ -19,11 +19,8 @@ import org.eclipse.sw360.datahandler.thrift.users.User; import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; -import com.cloudant.client.api.views.Key; -import com.cloudant.client.api.views.UnpaginatedRequestBuilder; -import com.cloudant.client.api.views.ViewRequest; -import com.cloudant.client.api.views.ViewRequestBuilder; -import com.cloudant.client.api.views.ViewResponse; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; +import com.ibm.cloud.cloudant.v1.model.ViewResult; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -183,18 +180,19 @@ public ComponentRepository(DatabaseConnectorCloudant db, ReleaseRepository relea } public List getRecentComponentsSummary(int limit, User user) { - ViewRequestBuilder query = getConnector().createQuery(Component.class, "byCreatedOn"); - UnpaginatedRequestBuilder unPagnReques = query.newRequest(Key.Type.STRING, Object.class).includeDocs(true).descending(true); + PostViewOptions.Builder queryBuilder = getConnector().getPostViewQueryBuilder(Component.class, "byCreatedOn") + .includeDocs(true) + .descending(true); if (limit >= 0){ - unPagnReques.limit(limit); + queryBuilder.limit(limit); } - List components = new ArrayList(getFullDocsById(queryForIdsAsValue(unPagnReques))); + List components = new ArrayList<>(getFullDocsById(queryForIdsAsValue(queryBuilder.build()))); return makeSummaryWithPermissionsFromFullDocs(SummaryType.SUMMARY, components, user); } public Set getUsedAttachmentContents() { - return queryForIdsAsValue(getConnector().createQuery(Component.class, "usedAttachmentContents")); + return queryForIdsAsValue(getConnector().getPostViewQueryBuilder(Component.class, "usedAttachmentContents").build()); } public Collection getMyComponents(String user) { @@ -254,7 +252,7 @@ public Set getUsingComponents(String releaseId) { public Component getComponentFromFossologyUploadId(String fossologyUploadId) { final Set componentIdList = queryForIdsAsValue("byFossologyId", fossologyUploadId); - if (componentIdList != null && componentIdList.size() > 0) + if (componentIdList != null && !componentIdList.isEmpty()) return get(CommonUtils.getFirst(componentIdList)); return null; } @@ -277,56 +275,50 @@ public Set searchByExternalIds(Map> externalIds) public Map> getRecentComponentsSummary(User user, PaginationData pageData) { final int rowsPerPage = pageData.getRowsPerPage(); + final int offset = pageData.getDisplayStart(); Map> result = Maps.newHashMap(); List components = Lists.newArrayList(); final boolean ascending = pageData.isAscending(); final int sortColumnNo = pageData.getSortColumnNumber(); - ViewRequestBuilder query; + PostViewOptions.Builder query; switch (sortColumnNo) { - case -1: - query = getConnector().createQuery(Component.class, "byCreatedOn"); - break; - case 0: - query = getConnector().createQuery(Component.class, "byvendor"); - break; - case 1: - query = getConnector().createQuery(Component.class, "byname"); - break; - case 2: - query = getConnector().createQuery(Component.class, "bymainlicense"); - break; - case 3: - query = getConnector().createQuery(Component.class, "bycomponenttype"); - break; - default: - query = getConnector().createQuery(Component.class, "all"); - break; + case -1: + query = getConnector().getPostViewQueryBuilder(Component.class, "byCreatedOn"); + break; + case 0: + query = getConnector().getPostViewQueryBuilder(Component.class, "byvendor"); + break; + case 1: + query = getConnector().getPostViewQueryBuilder(Component.class, "byname"); + break; + case 2: + query = getConnector().getPostViewQueryBuilder(Component.class, "bymainlicense"); + break; + case 3: + query = getConnector().getPostViewQueryBuilder(Component.class, "bycomponenttype"); + break; + default: + query = getConnector().getPostViewQueryBuilder(Component.class, "all"); + break; } - ViewRequest request = null; + PostViewOptions request; if (rowsPerPage == -1) { - request = query.newRequest(Key.Type.STRING, Object.class).descending(!ascending).includeDocs(true).build(); + request = query.descending(!ascending).includeDocs(true).build(); } else { - request = query.newPaginatedRequest(Key.Type.STRING, Object.class).rowsPerPage(rowsPerPage) + request = query.limit(rowsPerPage).skip(offset) .descending(!ascending).includeDocs(true).build(); } - ViewResponse response = null; try { - response = request.getResponse(); - int pageNo = pageData.getDisplayStart() / rowsPerPage; - int i = 1; - while (i <= pageNo) { - response = response.nextPage(); - i++; - } - components = response.getDocsAs(Component.class); - } catch (Exception e) { + ViewResult response = getConnector().getPostViewQueryResponse(request); + components = getPojoFromViewResponse(response); + pageData.setTotalRowCount(response.getTotalRows()); + } catch (ServiceConfigurationError e) { log.error("Error getting recent components", e); } components = makeSummaryWithPermissionsFromFullDocs(SummaryType.SUMMARY, components, user); - pageData.setTotalRowCount(response.getTotalRowCount()); result.put(pageData, components); return result; } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ConfigContainerRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ConfigContainerRepository.java index de402e5e8e..fc3158c8b5 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ConfigContainerRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ConfigContainerRepository.java @@ -15,10 +15,9 @@ import org.eclipse.sw360.datahandler.thrift.ConfigFor; import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; -import com.cloudant.client.api.views.Key; -import com.cloudant.client.api.views.UnpaginatedRequestBuilder; -import com.cloudant.client.api.views.ViewRequestBuilder; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -38,11 +37,11 @@ public ConfigContainerRepository(DatabaseConnectorCloudant databaseConnector) { } public ConfigContainer getByConfigFor(ConfigFor configFor) { - ViewRequestBuilder query = getConnector().createQuery(ConfigContainer.class, "byConfigFor"); - UnpaginatedRequestBuilder reqBuilder = query.newRequest(Key.Type.STRING, Object.class) - .keys(configFor.toString()).includeDocs(true); + PostViewOptions query = getConnector().getPostViewQueryBuilder(ConfigContainer.class, "byConfigFor") + .keys(Collections.singletonList(configFor.toString())) + .includeDocs(true).build(); - List configs = queryView(reqBuilder); + List configs = queryView(query); if (configs.size() != 1) { throw new IllegalStateException( "There are " + configs.size() + " configuration objects in the couch db for type " + configFor diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageRepository.java index 6c2e0fbc78..14f9f6b30f 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageRepository.java @@ -21,13 +21,10 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.PaginationData; import org.eclipse.sw360.datahandler.thrift.packages.Package; -import org.eclipse.sw360.datahandler.thrift.users.User; import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; -import com.cloudant.client.api.views.Key; -import com.cloudant.client.api.views.ViewRequest; -import com.cloudant.client.api.views.ViewRequestBuilder; -import com.cloudant.client.api.views.ViewResponse; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; +import com.ibm.cloud.cloudant.v1.model.ViewResult; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -127,52 +124,46 @@ public List searchByPurl(String purl, boolean caseInsenstive) { public Map> getPackagesWithPagination(PaginationData pageData) { final int rowsPerPage = pageData.getRowsPerPage(); + final int offset = pageData.getDisplayStart(); Map> result = Maps.newHashMap(); List packages = Lists.newArrayList(); final boolean ascending = pageData.isAscending(); final int sortColumnNo = pageData.getSortColumnNumber(); - ViewRequestBuilder query; + PostViewOptions.Builder query; switch (sortColumnNo) { - case -1: - query = getConnector().createQuery(Package.class, "byCreatedOn"); - break; - case 0: - query = getConnector().createQuery(Package.class, "byNameLowerCase"); - break; - case 3: - query = getConnector().createQuery(Package.class, "byLicenseIds"); - break; - case 4: - query = getConnector().createQuery(Package.class, "byPackageManager"); - break; - default: - query = getConnector().createQuery(Package.class, "all"); - break; + case -1: + query = getConnector().getPostViewQueryBuilder(Package.class, "byCreatedOn"); + break; + case 0: + query = getConnector().getPostViewQueryBuilder(Package.class, "byNameLowerCase"); + break; + case 3: + query = getConnector().getPostViewQueryBuilder(Package.class, "byLicenseIds"); + break; + case 4: + query = getConnector().getPostViewQueryBuilder(Package.class, "byPackageManager"); + break; + default: + query = getConnector().getPostViewQueryBuilder(Package.class, "all"); + break; } - ViewRequest request = null; + PostViewOptions request; if (rowsPerPage == -1) { - request = query.newRequest(Key.Type.STRING, Object.class).descending(!ascending).includeDocs(true).build(); + request = query.descending(!ascending).includeDocs(true).build(); } else { - request = query.newPaginatedRequest(Key.Type.STRING, Object.class).rowsPerPage(rowsPerPage) + request = query.limit(rowsPerPage).skip(offset) .descending(!ascending).includeDocs(true).build(); } - ViewResponse response = null; try { - response = request.getResponse(); - int pageNo = pageData.getDisplayStart() / rowsPerPage; - int i = 1; - while (i <= pageNo) { - response = response.nextPage(); - i++; - } - packages = response.getDocsAs(Package.class); + ViewResult response = getConnector().getPostViewQueryResponse(request); + packages = getPojoFromViewResponse(response); + pageData.setTotalRowCount(response.getTotalRows()); } catch (Exception e) { log.error("Error getting packages from repository: ", e); } - pageData.setTotalRowCount(response.getTotalRowCount()); result.put(pageData, packages); return result; } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectRepository.java index ac467c55f7..6a5fdf9836 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectRepository.java @@ -10,7 +10,6 @@ */ package org.eclipse.sw360.datahandler.db; -import org.apache.thrift.TException; import org.eclipse.sw360.components.summary.ProjectSummary; import org.eclipse.sw360.components.summary.SummaryType; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; @@ -20,7 +19,6 @@ import org.eclipse.sw360.datahandler.permissions.PermissionUtils; import org.eclipse.sw360.datahandler.permissions.ProjectPermissions; import org.eclipse.sw360.datahandler.thrift.PaginationData; -import org.eclipse.sw360.datahandler.thrift.ThriftClients; import org.eclipse.sw360.datahandler.thrift.projects.Project; import org.eclipse.sw360.datahandler.thrift.projects.ProjectData; import org.eclipse.sw360.datahandler.thrift.projects.ProjectService; @@ -29,16 +27,14 @@ import org.jetbrains.annotations.NotNull; import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; +import com.ibm.cloud.cloudant.v1.model.ViewResult; import com.cloudant.client.api.query.PredicateExpression; import com.cloudant.client.api.query.PredicatedOperation; import com.cloudant.client.api.query.QueryBuilder; import com.cloudant.client.api.query.QueryResult; import com.cloudant.client.api.query.Selector; import com.cloudant.client.api.query.Sort; -import com.cloudant.client.api.views.Key; -import com.cloudant.client.api.views.ViewRequest; -import com.cloudant.client.api.views.ViewRequestBuilder; -import com.cloudant.client.api.views.ViewResponse; import com.google.common.base.Joiner; import com.google.common.collect.Maps; @@ -457,7 +453,7 @@ public Map> getAccessibleProjectsSummary(User user qb.limit(rowsPerPage); } qb.skip(pageData.getDisplayStart()); - ViewRequestBuilder queryView = null; + PostViewOptions.Builder queryView = null; switch (sortColumnNo) { case 0: qb = qb.useIndex("byName"); @@ -476,32 +472,19 @@ public Map> getAccessibleProjectsSummary(User user break; case 3: case 4: - queryView = getConnector().createQuery(Project.class, "byState"); + queryView = getConnector().getPostViewQueryBuilder(Project.class, "byState"); break; default: break; } try { if (queryView != null) { - ViewRequest request = queryView.newPaginatedRequest(Key.Type.STRING, Object.class) - .rowsPerPage(rowsPerPage).descending(!ascending).includeDocs(true).build(); - ViewResponse response = request.getResponse(); - response = request.getResponse(); - int pageNo = pageData.getDisplayStart() / rowsPerPage; - List proj = new ArrayList(); - int count = pageNo == 0 ? rowsPerPage : (pageNo + 1) * rowsPerPage; - while (projects.size() < count) { - if (response != null) { - proj = response.getDocsAs(Project.class); - } - proj = proj.stream().filter(ProjectPermissions.isVisible(user)).collect(Collectors.toList()); - projects.addAll(proj.stream().collect(Collectors.toList())); - response = response.nextPage(); - if (response == null) { - break; - } + PostViewOptions request = queryView.limit(rowsPerPage).skip(pageData.getDisplayStart()) + .descending(!ascending).includeDocs(true).build(); + ViewResult response = getConnector().getPostViewQueryResponse(request); + if (response != null) { + projects = getPojoFromViewResponse(response); } - projects = projects.stream().skip(pageData.getDisplayStart()).limit(rowsPerPage).collect(Collectors.toList()); } else { QueryResult queryResult = getConnector().getQueryResult(query, Project.class); projects = queryResult.getDocs(); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/RelationsUsageRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/RelationsUsageRepository.java index f4a365cd97..2a418d7777 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/RelationsUsageRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/RelationsUsageRepository.java @@ -9,6 +9,7 @@ */ package org.eclipse.sw360.datahandler.db; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -18,9 +19,7 @@ import org.eclipse.sw360.datahandler.thrift.projects.UsedReleaseRelations; import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; -import com.cloudant.client.api.views.Key; -import com.cloudant.client.api.views.UnpaginatedRequestBuilder; -import com.cloudant.client.api.views.ViewRequestBuilder; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; /** * CRUD access for the RelationsUsageRepository class @@ -49,8 +48,8 @@ public RelationsUsageRepository(DatabaseConnectorCloudant db) { } public List getUsedRelationsByProjectId(String projectId) { - ViewRequestBuilder viewQuery = getConnector().createQuery(UsedReleaseRelations.class, "byProjectId"); - UnpaginatedRequestBuilder req = viewQuery.newRequest(Key.Type.STRING, Object.class).includeDocs(true).reduce(false).keys(projectId); - return queryView(req); + PostViewOptions viewQuery = getConnector().getPostViewQueryBuilder(UsedReleaseRelations.class, "byProjectId") + .includeDocs(true).reduce(false).keys(Collections.singletonList(projectId)).build(); + return queryView(viewQuery); } } \ No newline at end of file diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseRepository.java index 0a34660e0e..0a50317a29 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseRepository.java @@ -20,9 +20,8 @@ import java.util.*; import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; -import com.cloudant.client.api.views.Key; -import com.cloudant.client.api.views.UnpaginatedRequestBuilder; -import com.cloudant.client.api.views.ViewRequestBuilder; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; +import com.ibm.cloud.cloudant.v1.model.ViewResult; import java.util.stream.Collectors; @@ -30,8 +29,6 @@ import com.google.common.collect.Maps; import com.google.common.collect.Lists; -import com.cloudant.client.api.views.ViewRequest; -import com.cloudant.client.api.views.ViewResponse; /** * CRUD access for the Release class @@ -214,10 +211,10 @@ public List getReleaseSummary() { } public List getRecentReleases() { - ViewRequestBuilder query = getConnector().createQuery(Release.class, "byCreatedOn"); // Get the 5 last documents - UnpaginatedRequestBuilder reqBuilder = query.newRequest(Key.Type.STRING, Object.class).limit(5).descending(true).includeDocs(false); - return makeSummary(SummaryType.SHORT, queryForIds(reqBuilder)); + PostViewOptions query = getConnector().getPostViewQueryBuilder(Release.class, "byCreatedOn") + .limit(5).descending(true).includeDocs(false).build(); + return makeSummary(SummaryType.SHORT, queryForIds(query)); } public List getSubscribedReleases(String email) { @@ -262,13 +259,10 @@ public List getReleasesFromVendorIds(Set ids) { } public Set getReleaseIdsFromVendorIds(Set ids) { - ViewRequestBuilder query = getConnector().createQuery(Release.class, "releaseIdsByVendorId"); - String[] arrayOfString = new String[ids.size()]; - int index = 0; - for (String str : ids) - arrayOfString[index++] = str; - UnpaginatedRequestBuilder reqBuild = query.newRequest(Key.Type.STRING, Object.class).keys(arrayOfString); - return queryForIds(reqBuild); + PostViewOptions query = getConnector().getPostViewQueryBuilder(Release.class, "releaseIdsByVendorId") + .keys(ids.stream().map(r -> (Object)r).toList()) + .build(); + return queryForIds(query); } public Set getReleasesByVendorId(String vendorId) { @@ -314,60 +308,55 @@ public Map> getAccessibleReleasesWithPagination(Us final boolean ascending = pageData.isAscending(); final int sortColumnNo = pageData.getSortColumnNumber(); - ViewRequestBuilder query; + PostViewOptions.Builder query; switch (sortColumnNo) { - case -1: - query = getConnector().createQuery(Release.class, "byCreatedOn"); - break; - case 0: - query = getConnector().createQuery(Release.class, "byStatus"); - break; - case 1: - query = getConnector().createQuery(Release.class, "byname"); - break; - case 2: - query = getConnector().createQuery(Release.class, "releaseByVersion"); - break; - case 3: - query = getConnector().createQuery(Release.class, "byCreatorGroup"); - break; - case 4: - query = getConnector().createQuery(Release.class, "byECCAssessorContactPerson"); - break; - case 5: - query = getConnector().createQuery(Release.class, "byECCAssessorGroup"); - break; - case 6: - query = getConnector().createQuery(Release.class, "byECCAssessmentDate"); - break; - default: - query = getConnector().createQuery(Release.class, "all"); - break; + case -1: + query = getConnector().getPostViewQueryBuilder(Release.class, "byCreatedOn"); + break; + case 0: + query = getConnector().getPostViewQueryBuilder(Release.class, "byStatus"); + break; + case 1: + query = getConnector().getPostViewQueryBuilder(Release.class, "byname"); + break; + case 2: + query = getConnector().getPostViewQueryBuilder(Release.class, "releaseByVersion"); + break; + case 3: + query = getConnector().getPostViewQueryBuilder(Release.class, "byCreatorGroup"); + break; + case 4: + query = getConnector().getPostViewQueryBuilder(Release.class, "byECCAssessorContactPerson"); + break; + case 5: + query = getConnector().getPostViewQueryBuilder(Release.class, "byECCAssessorGroup"); + break; + case 6: + query = getConnector().getPostViewQueryBuilder(Release.class, "byECCAssessmentDate"); + break; + default: + query = getConnector().getPostViewQueryBuilder(Release.class, "all"); + break; } - ViewRequest request = null; + PostViewOptions request = null; if (rowsPerPage == -1) { - request = query.newRequest(Key.Type.STRING, Object.class).descending(!ascending).includeDocs(true).build(); + request = query.descending(!ascending).includeDocs(true).build(); } else { - request = query.newPaginatedRequest(Key.Type.STRING, Object.class).rowsPerPage(rowsPerPage) + request = query.limit(rowsPerPage).skip(pageData.getDisplayStart()) .descending(!ascending).includeDocs(true).build(); } - ViewResponse response = null; try { - response = request.getResponse(); - int pageNo = pageData.getDisplayStart() / rowsPerPage; - int i = 1; - while (i <= pageNo) { - response = response.nextPage(); - i++; + ViewResult response = getConnector().getPostViewQueryResponse(request); + if (response != null) { + releases = getPojoFromViewResponse(response); + pageData.setTotalRowCount(response.getTotalRows()); } - releases = response.getDocsAs(Release.class); } catch (Exception e) { log.error("Error getting recent releases", e); } releases = makeSummaryWithPermissionsFromFullDocs(SummaryType.SUMMARY, releases, user); - pageData.setTotalRowCount(response.getTotalRowCount()); result.put(pageData, releases); return result; } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/RepositoryUtils.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/RepositoryUtils.java index 8da2d775c9..b37c97ca50 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/RepositoryUtils.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/RepositoryUtils.java @@ -15,7 +15,7 @@ import org.eclipse.sw360.datahandler.thrift.RequestSummary; import org.eclipse.sw360.datahandler.thrift.users.User; -import com.cloudant.client.api.model.Response; +import com.ibm.cloud.cloudant.v1.model.DocumentResult; import java.util.*; @@ -30,7 +30,7 @@ public static RequestSummary doBulk(Collection objects, User user, DatabaseRe RequestSummary requestSummary = new RequestSummary(); if(PermissionUtils.isAdmin(user)) { // Prepare component for database - final List documentOperationResults = repository.executeBulk(objects); + final List documentOperationResults = repository.executeBulk(objects); requestSummary.setTotalElements(objects.size() ); requestSummary.setTotalAffectedElements(objects.size() - documentOperationResults.size()); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserRepository.java index d2f9e19610..fb3c244f9a 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserRepository.java @@ -21,13 +21,13 @@ import org.eclipse.sw360.datahandler.thrift.users.User; import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; +import com.ibm.cloud.cloudant.v1.model.ViewResultRow; import com.cloudant.client.api.query.Expression; import com.cloudant.client.api.query.QueryBuilder; import com.cloudant.client.api.query.QueryResult; import com.cloudant.client.api.query.Selector; import com.cloudant.client.api.query.Sort; -import com.cloudant.client.api.views.Key; -import com.cloudant.client.api.views.ViewRequest; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -194,12 +194,16 @@ public Map> getUsersWithPagination(PaginationData pag private Set getResultBasedOnQuery(String queryName) { Set userResults = Sets.newHashSet(); - ViewRequest query = getConnector().createQuery(User.class, queryName) - .newRequest(Key.Type.STRING, Object.class).includeDocs(false).build(); + PostViewOptions query = getConnector().getPostViewQueryBuilder(User.class, queryName) + .includeDocs(false).build(); try { - userResults = Sets.newTreeSet(CommonUtils.nullToEmptyList(query.getResponse().getKeys()).stream() - .filter(Objects::nonNull).collect(Collectors.toList())); - } catch (IOException e) { + userResults = getConnector().getPostViewQueryResponse(query).getRows() + .stream() + .map(ViewResultRow::getKey) + .filter(Objects::nonNull) + .map(Object::toString) + .collect(Collectors.toSet()); + } catch (ServiceConfigurationError e) { log.error("Error getting record of users based on queryName - " + queryName, e); } return userResults; diff --git a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationRequestRepository.java b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationRequestRepository.java index 4c00ab8899..ae326e2cc9 100644 --- a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationRequestRepository.java +++ b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationRequestRepository.java @@ -33,6 +33,10 @@ import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; +import com.ibm.cloud.cloudant.v1.model.ViewResult; +import com.ibm.cloud.cloudant.v1.model.ViewResultRow; +import com.ibm.cloud.sdk.core.service.exception.ServiceResponseException; import com.cloudant.client.api.query.Expression; import com.cloudant.client.api.query.PredicateExpression; import com.cloudant.client.api.query.PredicatedOperation; @@ -40,10 +44,6 @@ import com.cloudant.client.api.query.QueryResult; import com.cloudant.client.api.query.Selector; import com.cloudant.client.api.query.Sort; -import com.cloudant.client.api.views.Key; -import com.cloudant.client.api.views.Key.ComplexKey; -import com.cloudant.client.api.views.ViewRequest; -import com.cloudant.client.api.views.ViewResponse; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -245,14 +245,14 @@ private Map> queryViewWithPagination(Str return result; } - private List prepareKeys(String moderator, boolean ascending) { - ComplexKey startKey, endKey; + private List prepareKeys(String moderator, boolean ascending) { + String[] startKey, endKey; if (ascending) { - startKey = Key.complex(new String[] { moderator }); - endKey = Key.complex(new String[] { moderator, "\ufff0" }); + startKey = new String[] { moderator }; + endKey = new String[] { moderator, "\ufff0" }; } else { - startKey = Key.complex(new String[] { moderator, "\ufff0" }); - endKey = Key.complex(new String[] { moderator }); + startKey = new String[] { moderator, "\ufff0" }; + endKey = new String[] { moderator }; } return List.of(startKey, endKey); } @@ -292,21 +292,26 @@ public List getRequestsByRequestingUserWithPagination(String public Map getCountByModerationState(String moderator) { Map countByModerationState = Maps.newHashMap(); - List keys = prepareKeys(moderator, true); - ViewRequest countReq = getConnector() - .createQuery(ModerationRequest.class, "countByModerationState").newRequest(Key.Type.COMPLEX, Long.class) - .startKey(keys.get(0)).endKey(keys.get(1)).group(true).groupLevel(2).reduce(true).build(); + List keys = prepareKeys(moderator, true); + PostViewOptions countReq = getConnector() + .getPostViewQueryBuilder(ModerationRequest.class, "countByModerationState") + .startKey(keys.get(0)) + .endKey(keys.get(1)) + .group(true) + .groupLevel(2) + .descending(false) + .reduce(true).build(); try { - ViewResponse response = countReq.getResponse(); + ViewResult response = getConnector().getPostViewQueryResponse(countReq); if (null != response) { countByModerationState = response.getRows().stream().collect(Collectors.toMap(key -> { - String json = key.getKey().toJson(); + String json = key.getKey().toString(); String replace = json.replace("[", "").replace("]", "").replaceAll("\"", ""); - List moderatorToModStatus = new ArrayList(Arrays.asList(replace.split(","))); + List moderatorToModStatus = new ArrayList<>(Arrays.asList(replace.split(","))); return moderatorToModStatus.get(1); - }, val -> val.getValue())); + }, val -> Long.parseLong(val.getValue().toString()))); } - } catch (IOException e) { + } catch (ServiceResponseException e) { log.error("Error getting count of moderation requests based on moderation state", e); } return countByModerationState; @@ -315,19 +320,20 @@ public Map getCountByModerationState(String moderator) { public Map getCountByRequester(String user) { Map countByModerationState = Maps.newHashMap(); - List keys = prepareKeys(user, true); - ViewRequest countReq = getConnector() - .createQuery(ModerationRequest.class, "countByRequester").newRequest(Key.Type.COMPLEX, Long.class) - .startKey(keys.get(0)).endKey(keys.get(1)).group(true).groupLevel(2).reduce(true).build(); + List keys = prepareKeys(user, true); + PostViewOptions countReq = getConnector() + .getPostViewQueryBuilder(ModerationRequest.class, "countByRequester") + .startKey(keys.get(0)).endKey(keys.get(1)) + .descending(false).group(true).groupLevel(2).reduce(true).build(); try { - ViewResponse response = countReq.getResponse(); + ViewResult response = getConnector().getPostViewQueryResponse(countReq); if (null != response) { countByModerationState = response.getRows().stream().collect(Collectors.toMap(key -> { - String json = key.getKey().toJson(); + String json = key.getKey().toString(); return json.replaceAll("[\\[\\]\"]", ""); - }, ViewResponse.Row::getValue)); + }, val -> Long.parseLong(val.getValue().toString()))); } - } catch (IOException e) { + } catch (ServiceResponseException e) { log.error("Error getting count of moderation requests based on moderation state", e); } return countByModerationState; @@ -335,13 +341,17 @@ public Map getCountByRequester(String user) { public Set getRequestingUserDepts() { Set requestingUserDepts = Sets.newHashSet(); - ViewRequest query = getConnector() - .createQuery(ModerationRequest.class, "byRequestingUsersDeptView") - .newRequest(Key.Type.STRING, Object.class).includeDocs(false).build(); + PostViewOptions query = getConnector() + .getPostViewQueryBuilder(ModerationRequest.class, "byRequestingUsersDeptView") + .includeDocs(false).build(); try { - requestingUserDepts = Sets.newTreeSet(CommonUtils.nullToEmptyList(query.getResponse().getKeys()).stream() - .filter(Objects::nonNull).collect(Collectors.toList())); - } catch (IOException e) { + requestingUserDepts = getConnector().getPostViewQueryResponse(query).getRows() + .stream() + .map(ViewResultRow::getKey) + .filter(Objects::nonNull) + .map(Object::toString) + .collect(Collectors.toCollection(Sets::newTreeSet)); + } catch (ServiceResponseException e) { log.error("Error getting requesting users", e); } return requestingUserDepts; diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java index 7b771bcd8e..e68feae20b 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java @@ -23,6 +23,7 @@ import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.thrift.ThriftUtils; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; +import org.jetbrains.annotations.NotNull; import java.io.InputStream; import java.lang.reflect.Field; @@ -63,8 +64,7 @@ public String getDbName() { public void update(Object document) { DocumentResult resp; if (document != null) { - final Class documentClass = document.getClass(); - if (ThriftUtils.isMapped(documentClass)) { + if (ThriftUtils.isMapped(document.getClass())) { AttachmentContent content = (AttachmentContent) document; InputStream in = getAttachment(content.getId(), content.getFilename()); resp = this.updateWithResponse(document); @@ -103,7 +103,7 @@ public DatabaseInstanceCloudant getInstance() { return instance; } - public Set getAllIds(Class type) { + public Set getAllIds(@NotNull Class type) { Set ids = Sets.newHashSet(); try { PostViewOptions viewOptions = new PostViewOptions.Builder() @@ -143,8 +143,8 @@ public T get(Class type, String id) { } if (extractedType != null) { final String entityType = extractedType.toLowerCase(); - if (!entitiesWithNonMatchingStructType.stream().map(String::toLowerCase) - .anyMatch(tye -> tye.equals(entityType)) + if (entitiesWithNonMatchingStructType.stream().map(String::toLowerCase) + .noneMatch(tye -> tye.equals(entityType)) && !type.getSimpleName().equalsIgnoreCase(extractedType)) { return null; } @@ -255,7 +255,7 @@ public List get(Class type, Collection ids, boolean ignoreNotF return get(type, ids); } - public List executeBulk(Collection list) { + public List executeBulk(@NotNull Collection list) { BulkDocs bulkDocs = new BulkDocs.Builder() .docs(list.stream().map(this::getDocumentFromPojo).collect(Collectors.toList())) .build(); @@ -286,7 +286,7 @@ public List executeBulk(Collection list) { return responses; } - public List deleteBulk(Collection deletionCandidates) { + public List deleteBulk(@NotNull Collection deletionCandidates) { BulkDocs bulkDocs = new BulkDocs.Builder() .docs( deletionCandidates.stream() @@ -307,7 +307,7 @@ public List deleteIds(Collection ids) { return deleteBulk(deletionCandidates); } - public int getDocumentCount(Class type) { + public int getDocumentCount(@NotNull Class type) { int count = 0; try { PostViewOptions viewOption = new PostViewOptions.Builder() @@ -325,7 +325,7 @@ public int getDocumentCount(Class type) { return count; } - public int getDocumentCount(Class type, String viewName, String[] keys) { + public int getDocumentCount(@NotNull Class type, String viewName, String[] keys) { int count = 0; try { PostViewOptions viewOption = new PostViewOptions.Builder() @@ -349,8 +349,18 @@ public int getDocumentCount(Class type, String viewName, String[] keys) { return count; } - public ViewRequestBuilder createQuery(Class type, String queryName) { - return database.getViewRequestBuilder(type.getSimpleName(), queryName); + public PostViewOptions.Builder getPostViewQueryBuilder( + @NotNull Class type, String queryName) { + return new PostViewOptions.Builder() + .db(this.dbName) + .ddoc(type.getSimpleName()) + .view(queryName); + } + + public ViewResult getPostViewQueryResponse(PostViewOptions options) { + return this.instance.getClient().postView(options) + .execute() + .getResult(); } public InputStream getAttachment(String docId, String attachmentName) { @@ -463,7 +473,7 @@ public QueryResult getQueryResult(String query, Class type) { return database.query(query, type); } - public Set getDistinctSortedStringKeys(Class type, String viewName) { + public Set getDistinctSortedStringKeys(@NotNull Class type, String viewName) { PostViewOptions viewOptions = new PostViewOptions.Builder() .db(this.dbName) .ddoc(type.getSimpleName()) @@ -502,13 +512,16 @@ public List getDocsByListIds(Class type, Collection ids) { } public Document getDocumentFromPojo(Object document) { + if (document instanceof Document) { + return (Document) document; + } Document doc = new Document(); Gson gson = this.instance.getGson(); doc.setProperties(gson.fromJson(gson.toJson(document), Map.class)); return doc; } - public T getPojoFromDocument(Document document, Class type) { + public T getPojoFromDocument(@NotNull Document document, Class type) { return this.instance.getGson().fromJson(document.toString(), type); } diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseRepositoryCloudantClient.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseRepositoryCloudantClient.java index 3959a8df2c..c39fc67da0 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseRepositoryCloudantClient.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseRepositoryCloudantClient.java @@ -9,7 +9,8 @@ */ package org.eclipse.sw360.datahandler.cloudantclient; -import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -28,8 +29,14 @@ import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import com.ibm.cloud.cloudant.v1.model.IndexDefinition; import com.ibm.cloud.cloudant.v1.model.IndexField; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; +import com.ibm.cloud.cloudant.v1.model.ViewResult; +import com.ibm.cloud.cloudant.v1.model.ViewResultRow; +import com.ibm.cloud.sdk.core.service.exception.ServiceResponseException; import com.google.common.collect.Lists; import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import org.jetbrains.annotations.NotNull; /** * Access the database in a CRUD manner, for a generic class @@ -85,14 +92,11 @@ protected DatabaseConnectorCloudant getConnector() { } public List queryByIds(String viewName, Collection ids) { - String[] idStrs = new String[ids.size()]; - int index = 0; - for (String str : ids) - idStrs[index++] = str; - ViewRequestBuilder query = connector.createQuery(type, viewName); - UnpaginatedRequestBuilder reqBuilder = query.newRequest(Key.Type.STRING, Object.class).includeDocs(true) - .keys(idStrs); - return queryView(reqBuilder); + PostViewOptions query = connector.getPostViewQueryBuilder(type, viewName) + .includeDocs(true) + .keys(ids.stream().map(r -> (Object)r).toList()) + .build(); + return queryView(query); } public DatabaseRepositoryCloudantClient(DatabaseConnectorCloudant connector, Class type) { @@ -101,86 +105,86 @@ public DatabaseRepositoryCloudantClient(DatabaseConnectorCloudant connector, Cla } public Set queryForIdsAsValue(String queryName, String key) { - ViewRequestBuilder query = connector.createQuery(type, queryName); + PostViewOptions.Builder query = connector.getPostViewQueryBuilder(type, queryName); return queryForIdsAsValue(query, key); } public Set queryForIdsAsValue(String queryName, Set keys) { - ViewRequestBuilder query = connector.createQuery(type, queryName); + PostViewOptions.Builder query = connector.getPostViewQueryBuilder(type, queryName); return queryForIdsAsValue(query, keys); } - public Set queryForIdsAsValue(ViewRequestBuilder query, Set keys) { - String[] arrayOfString = new String[keys.size()]; - int index = 0; - for (String str : keys) - arrayOfString[index++] = str; - UnpaginatedRequestBuilder req = query.newRequest(Key.Type.STRING, Object.class).keys(arrayOfString); + public Set queryForIdsAsValue(PostViewOptions.Builder query, Set keys) { + PostViewOptions req = query + .keys(keys.stream().map(r -> (Object)r).toList()) + .build(); return queryForIdsFromReqBuilder(req); } - public Set queryForIdsAsValue(ViewRequestBuilder query, String key) { - ViewResponse viewReponse = null; + public Set queryForIdsAsValue(PostViewOptions.Builder queryBuilder, String key) { + PostViewOptions query = queryBuilder.includeDocs(true).keys(Collections.singletonList(key)).build(); + ViewResult viewResponse = null; try { - viewReponse = query.newRequest(Key.Type.STRING, Object.class).includeDocs(true).keys(key).build() - .getResponse(); - } catch (IOException e) { + viewResponse = getConnector().getPostViewQueryResponse(query); + } catch (ServiceResponseException e) { log.error("Error executing query for ids as value", e); } HashSet ids = new HashSet<>(); - for (ViewResponse.Row row : viewReponse.getRows()) { - ids.add(row.getValue()); + if (viewResponse != null) { + for (ViewResultRow row : viewResponse.getRows()) { + ids.add(row.getValue()); + } } return ids.stream().map(Object::toString).collect(Collectors.toSet()); } - public Set queryForIdsAsValue(ViewRequestBuilder query) { - UnpaginatedRequestBuilder reqBuild = query.newRequest(Key.Type.STRING, Object.class).includeDocs(true); + public Set queryForIdsAsValue(PostViewOptions.Builder query) { + PostViewOptions reqBuild = query.includeDocs(true).build(); return queryForIdsFromReqBuilder(reqBuild); } public List queryView(String viewName, String startKey, String endKey) { - ViewRequestBuilder query = connector.createQuery(type, viewName); - UnpaginatedRequestBuilder reqBuild = query.newRequest(Key.Type.STRING, Object.class).startKey(startKey) - .endKey(endKey).includeDocs(true); - return queryView(reqBuild); + PostViewOptions query = connector.getPostViewQueryBuilder(type, viewName) + .startKey(startKey) + .endKey(endKey).includeDocs(true).build(); + return queryView(query); } public List queryView(String viewName, String key) { - ViewRequestBuilder query = connector.createQuery(type, viewName); - UnpaginatedRequestBuilder reqBuild = query.newRequest(Key.Type.STRING, Object.class).keys(key) - .includeDocs(true); - return queryView(reqBuild); + PostViewOptions query = connector.getPostViewQueryBuilder(type, viewName) + .keys(Collections.singletonList(key)) + .includeDocs(true).build(); + return queryView(query); } public List queryView(String viewName) { - ViewRequestBuilder query = connector.createQuery(type, viewName); - UnpaginatedRequestBuilder reqBuild = query.newRequest(Key.Type.STRING, Object.class).includeDocs(true); - return queryView(reqBuild); + PostViewOptions query = connector.getPostViewQueryBuilder(type, viewName) + .includeDocs(true).build(); + return queryView(query); } - public Set queryForIds(UnpaginatedRequestBuilder query) { - ViewResponse rows = queryQueryResponse(query); - HashSet ids = new HashSet<>(); - for (ViewResponse.Row row : rows.getRows()) { + public Set queryForIds(PostViewOptions query) { + ViewResult response = this.connector.getPostViewQueryResponse(query); + HashSet ids = new HashSet<>(); + for (ViewResultRow row : response.getRows()) { ids.add(row.getId()); } - return ids.stream().map(Object::toString).collect(Collectors.toSet()); + return ids; } - public Set queryForIdsAsValue(UnpaginatedRequestBuilder query) { - ViewResponse rows = queryQueryResponse(query); + public Set queryForIdsAsValue(PostViewOptions query) { + ViewResult response = this.connector.getPostViewQueryResponse(query); HashSet ids = new HashSet<>(); - for (ViewResponse.Row row : rows.getRows()) { + for (ViewResultRow row : response.getRows()) { ids.add(row.getValue()); } return ids.stream().map(Object::toString).collect(Collectors.toSet()); } - public Set queryForIdsFromReqBuilder(UnpaginatedRequestBuilder query) { - ViewResponse rows = queryQueryResponse(query); + public Set queryForIdsFromReqBuilder(PostViewOptions query) { + ViewResult response = queryQueryResponse(query); HashSet ids = new HashSet<>(); - for (ViewResponse.Row row : rows.getRows()) { + for (ViewResultRow row : response.getRows()) { ids.add(row.getValue()); } return ids.stream().map(Object::toString).collect(Collectors.toSet()); @@ -199,44 +203,43 @@ public Set queryForIdsAsValueByPrefix(String viewName, String prefix) { } public Set queryForIds(String queryName, String startKey, String endKey) { - ViewRequestBuilder query = connector.createQuery(type, queryName); - UnpaginatedRequestBuilder req = query.newRequest(Key.Type.STRING, Object.class).startKey(startKey) - .endKey(endKey); - return queryForIds(req); + PostViewOptions query = connector.getPostViewQueryBuilder(type, queryName) + .startKey(startKey) + .endKey(endKey) + .build(); + return queryForIds(query); } public Set queryForIdsAsValue(String queryName, String startKey, String endKey) { - ViewRequestBuilder query = connector.createQuery(type, queryName); - UnpaginatedRequestBuilder req = query.newRequest(Key.Type.STRING, Object.class).startKey(startKey) - .endKey(endKey); - return queryForIdsAsValue(req); + PostViewOptions query = connector.getPostViewQueryBuilder(type, queryName) + .startKey(startKey) + .endKey(endKey) + .build(); + return queryForIdsAsValue(query); } public Set queryForIds(String queryName, String key) { - ViewRequestBuilder query = connector.createQuery(type, queryName); - UnpaginatedRequestBuilder reqBuild = query.newRequest(Key.Type.STRING, Object.class).keys(key); - return queryForIds(reqBuild); + PostViewOptions query = connector.getPostViewQueryBuilder(type, queryName) + .keys(Collections.singletonList(key)).build(); + return queryForIds(query); } public Set queryForIdsAsComplexValue(String queryName, String... keys) { - ViewRequestBuilder query = connector.createQuery(type, queryName); - UnpaginatedRequestBuilder reqBuild = query.newRequest(Key.Type.STRING, Object.class).keys(keys); - return queryForIds(reqBuild); + PostViewOptions query = connector.getPostViewQueryBuilder(type, queryName) + .keys(Arrays.asList(keys)).build(); + return queryForIds(query); } public Set queryForIdsAsComplexValues(String queryName, Map> keys) { - Set complexKeys = keys.entrySet().stream().map(DatabaseRepositoryCloudantClient::createComplexKeys) - .flatMap(Collection::stream).collect(Collectors.toSet()); - ViewRequestBuilder query = connector.createQuery(type, queryName); - Key.ComplexKey[] complexKys = new Key.ComplexKey[complexKeys.size()]; - int index = 0; - for (String[] keyArr : complexKeys) { - Key.ComplexKey key = Key.complex(keyArr); - complexKys[index++] = key; - } - UnpaginatedRequestBuilder reqBuild = query.newRequest(Key.Type.COMPLEX, Object.class) - .keys(complexKys); - return queryForIds(reqBuild); + List complexKeys = keys.entrySet().stream() + .map(DatabaseRepositoryCloudantClient::createComplexKeys) + .flatMap(Collection::stream) + .map(r -> (Object)r) + .collect(Collectors.toList()); + PostViewOptions query = connector.getPostViewQueryBuilder(type, queryName) + .keys(complexKeys) + .build(); + return queryForIds(query); } public Set queryForIdsOnlyComplexKey(String queryName, String key) { @@ -246,116 +249,99 @@ public Set queryForIdsOnlyComplexKey(String queryName, String key) { public Set queryForIdsOnlyComplexKeys(String queryName, Set keys) { Set queryResult = new HashSet<>(); for (String key : keys) { - Key.ComplexKey startKeys = Key.complex(new String[] { key }); - Key.ComplexKey endKeys = Key.complex(new String[] { key, "\ufff0" }); + String[] startKeys = new String[] { key }; + String[] endKeys = new String[] { key, "\ufff0" }; queryResult.addAll(queryForIds(queryName, startKeys, endKeys)); } return queryResult; } - public Collection queryForIds(String queryName, ComplexKey startKeys, ComplexKey endKeys) { - ViewRequestBuilder query = connector.createQuery(type, queryName); - UnpaginatedRequestBuilder reqBuilder = query.newRequest(Key.Type.COMPLEX, Object.class).startKey(startKeys) - .endKey(endKeys); - return queryForIds(reqBuilder); + public Collection queryForIds(String queryName, String[] startKeys, String[] endKeys) { + PostViewOptions query = connector.getPostViewQueryBuilder(type, queryName) + .startKey(startKeys) + .endKey(endKeys) + .build(); + return queryForIds(query); } private static Set createComplexKeys(Map.Entry> key) { return key.getValue().stream().map(v -> new String[] { key.getKey(), v }).collect(Collectors.toSet()); } - public List queryView(UnpaginatedRequestBuilder req) { + public List queryView(PostViewOptions req) { List docList = Lists.newArrayList(); try { - docList = req.build().getResponse().getDocsAs(type); - } catch (NoDocumentException | IOException e) { + ViewResult response = getConnector().getPostViewQueryResponse(req); + docList = getPojoFromViewResponse(response); + } catch (ServiceResponseException e) { log.warn("Error in getting documents", e); } return docList; } - public List queryViewForSource(ViewRequest req) { + public @NotNull List getPojoFromViewResponse(@NotNull ViewResult response) { + return response.getRows().stream() + .map(r -> connector.getPojoFromDocument(r.getDoc(), type)) + .toList(); + } + + public List queryViewForSource(PostViewOptions req) { List sources = Lists.newArrayList(); Gson gson = new Gson(); try { - ViewResponse response = req.getResponse(); - for (ViewResponse.Row row : response.getRows()) { - Map srcMap = gson.fromJson(new Gson().toJson(row.getValue()), Map.class); + ViewResult response = getConnector().getPostViewQueryResponse(req); + for (ViewResultRow row : response.getRows()) { + Type t = new TypeToken>() {}.getType(); + Map srcMap = gson.fromJson(new Gson().toJson(row.getValue()), t); Source._Fields type = Source._Fields.findByName(srcMap.keySet().iterator().next()); - Source source = new Source(type, srcMap.values().iterator().next().toString()); + Source source = new Source(type, srcMap.values().iterator().next()); sources.add(source); } - } catch (NoDocumentException | IOException e) { + } catch (ServiceResponseException e) { log.warn("Error in getting source", e); } return sources; } - public List queryViewForAttchmnt(ViewRequest req) { - List attchmnts = Lists.newArrayList(); + public List queryViewForAttachment(PostViewOptions req) { + List attachments = Lists.newArrayList(); Gson gson = new Gson(); try { - ViewResponse response = req.getResponse(); - for (ViewResponse.Row row : response.getRows()) { + ViewResult response = getConnector().getPostViewQueryResponse(req); + for (ViewResultRow row : response.getRows()) { Attachment value = gson.fromJson(new Gson().toJson(row.getValue()), Attachment.class); - attchmnts.add(value); + attachments.add(value); } - } catch (NoDocumentException | IOException e) { + } catch (ServiceResponseException e) { log.warn("Error in getting attachment", e); } - return attchmnts; + return attachments; } - public ViewResponse queryQueryResponse(UnpaginatedRequestBuilder req) { - ViewResponse viewResp = null; + public ViewResult queryQueryResponse(PostViewOptions req) { + ViewResult viewResp = null; try { - viewResp = req.build().getResponse(); - } catch (NoDocumentException | IOException e) { + viewResp = getConnector().getPostViewQueryResponse(req); + } catch (ServiceResponseException e) { log.warn("Error in query execution", e); } return viewResp; } - public List multiRequestqueryView(MultipleRequestBuilder req) { - List docList = Lists.newArrayList(); - try { - List> responses = req.add().build().getViewResponses(); - for (ViewResponse response : responses) { - docList.addAll(response.getDocsAs(type)); - } - } catch (IOException e) { - log.error("Error executing multi request query view", e); - } - return docList; - } - - public List> multiRequestqueryViewResponse(MultipleRequestBuilder req) { - List> responses = null; + public ViewResult queryViewForComplexKeys(PostViewOptions req) { + ViewResult responses = null; try { - responses = req.add().build().getViewResponses(); - } catch (IOException e) { - log.error("Error executing multi request query view response", e); - } - return responses; - } - - public ViewResponse queryViewForComplexKeys( - UnpaginatedRequestBuilder req) { - ViewResponse responses = null; - try { - responses = req.build().getResponse(); - } catch (IOException e) { + responses = getConnector().getPostViewQueryResponse(req); + } catch (ServiceResponseException e) { log.error("Error executing query view with complex keys", e); } return responses; } - public ViewRequest buildRequest(ViewRequestBuilder viewQuery, Collection ids) { - String[] idStrs = new String[ids.size()]; - int index = 0; - for (String str : ids) - idStrs[index++] = str; - return viewQuery.newRequest(Key.Type.STRING, Object.class).includeDocs(false).keys(idStrs).build(); + public PostViewOptions buildRequest(PostViewOptions.Builder viewQuery, Collection ids) { + return viewQuery.includeDocs(false) + .keys(ids.stream().map(r -> (Object)r).toList()) + .build(); } public boolean add(T doc) { From 0e6d31c908ada8ccde1ad6ae913266b7af86f806 Mon Sep 17 00:00:00 2001 From: Gaurav Mishra Date: Thu, 15 Aug 2024 19:20:08 +0530 Subject: [PATCH 07/10] fix(cloudant): fix query builders Signed-off-by: Gaurav Mishra --- .../datahandler/db/ProjectRepository.java | 126 +++++----- .../sw360/datahandler/db/UserRepository.java | 122 +++++----- .../db/ModerationRequestRepository.java | 223 +++++++++--------- .../DatabaseConnectorCloudant.java | 73 +++++- 4 files changed, 295 insertions(+), 249 deletions(-) diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectRepository.java index 6a5fdf9836..6167e70f73 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectRepository.java @@ -27,24 +27,20 @@ import org.jetbrains.annotations.NotNull; import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import com.ibm.cloud.cloudant.v1.model.PostFindOptions; import com.ibm.cloud.cloudant.v1.model.PostViewOptions; import com.ibm.cloud.cloudant.v1.model.ViewResult; -import com.cloudant.client.api.query.PredicateExpression; -import com.cloudant.client.api.query.PredicatedOperation; -import com.cloudant.client.api.query.QueryBuilder; -import com.cloudant.client.api.query.QueryResult; -import com.cloudant.client.api.query.Selector; -import com.cloudant.client.api.query.Sort; import com.google.common.base.Joiner; import com.google.common.collect.Maps; import java.util.*; import java.util.stream.Collectors; -import static com.cloudant.client.api.query.Expression.eq; -import static com.cloudant.client.api.query.Operation.and; -import static com.cloudant.client.api.query.Operation.or; import static com.google.common.base.Strings.isNullOrEmpty; +import static org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant.elemMatch; +import static org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant.eq; +import static org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant.and; +import static org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant.or; import static org.eclipse.sw360.datahandler.common.SW360Utils.getBUFromOrganisation; /** @@ -392,90 +388,83 @@ public Map> getAccessibleProjectsSummary(User user List projects = new ArrayList<>(); Map> result = Maps.newHashMap(); - String query = null; - final Selector typeSelector = eq("type", "project"); - final Selector private_visibility_Selector = eq("visbility", "PRIVATE"); - final Selector createdBySelector = eq("createdBy", requestingUserEmail); - final Selector getAllPrivateProjects = and(private_visibility_Selector, createdBySelector); - final Selector everyone_visibility_Selector = eq("visbility", "EVERYONE"); - - final Selector isAProjectResponsible = eq("projectResponsible", requestingUserEmail); - final Selector isALeadArchitect = eq("leadArchitect", requestingUserEmail); - final Selector isAModerator = PredicatedOperation.elemMatch("moderators", - PredicateExpression.eq(requestingUserEmail)); - final Selector isAContributor = PredicatedOperation.elemMatch("contributors", - PredicateExpression.eq(requestingUserEmail)); - final Selector meAndModorator_visibility_Selector = eq("visbility", "ME_AND_MODERATORS"); - final Selector isUserBelongToMeAndModerator = and(meAndModorator_visibility_Selector, - or(createdBySelector, isAProjectResponsible, isALeadArchitect, isAModerator, isAContributor)); - - final Selector buAndModorator_visibility_Selector = eq("visbility", "BUISNESSUNIT_AND_MODERATORS"); - final Selector userBuSelector = eq("businessUnit", userBU); + PostFindOptions query = null; + final Map typeSelector = eq("type", "project"); + final Map private_visibility_Selector = eq("visbility", "PRIVATE"); + final Map createdBySelector = eq("createdBy", requestingUserEmail); + final Map getAllPrivateProjects = and(List.of(private_visibility_Selector, createdBySelector)); + final Map everyone_visibility_Selector = eq("visbility", "EVERYONE"); + + final Map isAProjectResponsible = eq("projectResponsible", requestingUserEmail); + final Map isALeadArchitect = eq("leadArchitect", requestingUserEmail); + final Map isAModerator = elemMatch("moderators", requestingUserEmail); + final Map isAContributor = elemMatch("contributors", requestingUserEmail); + final Map meAndModorator_visibility_Selector = eq("visbility", "ME_AND_MODERATORS"); + final Map isUserBelongToMeAndModerator = and(List.of(meAndModorator_visibility_Selector, + or(List.of(createdBySelector, isAProjectResponsible, isALeadArchitect, isAModerator, isAContributor)))); + + final Map buAndModorator_visibility_Selector = eq("visbility", "BUISNESSUNIT_AND_MODERATORS"); + final Map userBuSelector = eq("businessUnit", userBU); boolean isAdmin = PermissionUtils.isAdmin(user); boolean isClearingAdmin = PermissionUtils.isUserAtLeast(UserGroup.CLEARING_ADMIN, user); - Selector isUserBelongToBuAndModerator = null; + Map isUserBelongToBuAndModerator; - Selector[] buSelectors = null; + List> buSelectors = new ArrayList<>(); Map> secondaryDepartmentsAndRoles = user.getSecondaryDepartmentsAndRoles(); if (!CommonUtils.isNullOrEmptyMap(secondaryDepartmentsAndRoles)) { Set secondaryUgs = secondaryDepartmentsAndRoles.keySet(); Set secondaryBus = secondaryUgs.stream().map(SW360Utils::getBUFromOrganisation) .collect(Collectors.toSet()); - buSelectors = new Selector[secondaryBus.size() + 2]; - int index = 0; for (String secondaryBU : secondaryBus) { - Selector buselector = eq("businessUnit", secondaryBU); - buSelectors[index] = buselector; - index++; + buSelectors.add(eq("businessUnit", secondaryBU)); } - } else { - buSelectors = new Selector[2]; } - buSelectors[buSelectors.length - 2] = isUserBelongToMeAndModerator; - buSelectors[buSelectors.length - 1] = userBuSelector; - isUserBelongToBuAndModerator = and(buAndModorator_visibility_Selector, or(buSelectors)); + buSelectors.add(isUserBelongToMeAndModerator); + buSelectors.add(userBuSelector); + isUserBelongToBuAndModerator = and(List.of(buAndModorator_visibility_Selector, or(buSelectors))); - Selector finalSelector = null; + Map finalSelector; if (PermissionUtils.IS_ADMIN_PRIVATE_ACCESS_ENABLED && isAdmin) { finalSelector = typeSelector; } else { if (isClearingAdmin) { - finalSelector = and(typeSelector, or(getAllPrivateProjects, everyone_visibility_Selector, - isUserBelongToMeAndModerator, buAndModorator_visibility_Selector)); + finalSelector = and(List.of(typeSelector, or(List.of(getAllPrivateProjects, everyone_visibility_Selector, + isUserBelongToMeAndModerator, buAndModorator_visibility_Selector)))); } else { - finalSelector = and(typeSelector, or(getAllPrivateProjects, everyone_visibility_Selector, - isUserBelongToMeAndModerator, isUserBelongToBuAndModerator)); + finalSelector = and(List.of(typeSelector, or(List.of(getAllPrivateProjects, everyone_visibility_Selector, + isUserBelongToMeAndModerator, isUserBelongToBuAndModerator)))); } } - QueryBuilder qb = new QueryBuilder(finalSelector); + PostFindOptions.Builder qb = getConnector().getQueryBuilder() + .selector(finalSelector); if (rowsPerPage != -1) { qb.limit(rowsPerPage); } qb.skip(pageData.getDisplayStart()); PostViewOptions.Builder queryView = null; switch (sortColumnNo) { - case 0: - qb = qb.useIndex("byName"); - qb = ascending ? qb.sort(Sort.asc("name")) : qb.sort(Sort.desc("name")); - query = qb.build(); - break; - case 1: - qb = qb.useIndex("byDesc"); - qb = ascending ? qb.sort(Sort.asc("description")) : qb.sort(Sort.desc("description")); - query = qb.build(); - break; - case 2: - qb = qb.useIndex("byProjectResponsible"); - qb = ascending ? qb.sort(Sort.asc("projectResponsible")) : qb.sort(Sort.desc("projectResponsible")); - query = qb.build(); - break; - case 3: - case 4: - queryView = getConnector().getPostViewQueryBuilder(Project.class, "byState"); - break; - default: - break; + case 0: + qb.useIndex(Collections.singletonList("byName")) + .addSort(Collections.singletonMap("name", ascending ? "asc" : "desc")); + query = qb.build(); + break; + case 1: + qb.useIndex(Collections.singletonList("byDesc")) + .addSort(Collections.singletonMap("description", ascending ? "asc" : "desc")); + query = qb.build(); + break; + case 2: + qb.useIndex(Collections.singletonList("byProjectResponsible")) + .addSort(Collections.singletonMap("projectResponsible", ascending ? "asc" : "desc")); + query = qb.build(); + break; + case 3: + case 4: + queryView = getConnector().getPostViewQueryBuilder(Project.class, "byState"); + break; + default: + break; } try { if (queryView != null) { @@ -486,8 +475,7 @@ public Map> getAccessibleProjectsSummary(User user projects = getPojoFromViewResponse(response); } } else { - QueryResult queryResult = getConnector().getQueryResult(query, Project.class); - projects = queryResult.getDocs(); + projects = getConnector().getQueryResult(query, Project.class); } } catch (Exception e) { log.error("Error getting projects", e); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserRepository.java index fb3c244f9a..f136846d96 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserRepository.java @@ -9,9 +9,10 @@ */ package org.eclipse.sw360.datahandler.db; -import static com.cloudant.client.api.query.Expression.eq; -import static com.cloudant.client.api.query.Operation.and; -import static com.cloudant.client.api.query.Operation.or; +import static org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant.eq; +import static org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant.and; +import static org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant.exists; +import static org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant.or; import org.eclipse.sw360.components.summary.UserSummary; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; @@ -22,17 +23,12 @@ import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import com.ibm.cloud.cloudant.v1.model.PostViewOptions; +import com.ibm.cloud.cloudant.v1.model.PostFindOptions; import com.ibm.cloud.cloudant.v1.model.ViewResultRow; -import com.cloudant.client.api.query.Expression; -import com.cloudant.client.api.query.QueryBuilder; -import com.cloudant.client.api.query.QueryResult; -import com.cloudant.client.api.query.Selector; -import com.cloudant.client.api.query.Sort; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import java.io.IOException; import java.util.*; import java.util.stream.Collectors; @@ -215,76 +211,76 @@ private Map> queryViewWithPagination(PaginationData p List users = Lists.newArrayList(); final boolean ascending = pageData.isAscending(); final int sortColumnNo = pageData.getSortColumnNumber(); - String query = null; - final Selector typeSelector = eq("type", "user"); - final Selector emptySecondaryDepartmentsAndRolesSelector = or( - Expression.exists("secondaryDepartmentsAndRoles", false), eq("secondaryDepartmentsAndRoles", "")); - QueryBuilder qb = new QueryBuilder(typeSelector); + PostFindOptions query = null; + final Map typeSelector = Collections.singletonMap("type", + Collections.singletonMap("$eq", "user")); + final Map emptySecondaryDepartmentsAndRolesSelector = or( + List.of(exists("secondaryDepartmentsAndRoles", false), eq("secondaryDepartmentsAndRoles", ""))); + PostFindOptions.Builder qb = getConnector().getQueryBuilder() + .selector(typeSelector); if (rowsPerPage != -1) { qb.limit(rowsPerPage); } qb.skip(pageData.getDisplayStart()); switch (sortColumnNo) { - case -1: - case 2: - qb = qb.useIndex("byEmailUser"); - qb = ascending ? qb.sort(Sort.asc("email")) : qb.sort(Sort.desc("email")); - query = qb.build(); - break; - case 0: - qb = qb.useIndex("byFirstName"); - qb = ascending ? qb.sort(Sort.asc("givenname")) : qb.sort(Sort.desc("givenname")); - query = qb.build(); - break; - case 1: - qb = qb.useIndex("byLastName"); - qb = ascending ? qb.sort(Sort.asc("lastname")) : qb.sort(Sort.desc("lastname")); - query = qb.build(); - break; - case 3: - qb = qb.useIndex("byActiveStatus"); - qb = ascending ? qb.sort(Sort.asc("deactivated")) : qb.sort(Sort.desc("deactivated")); - query = qb.build(); - break; - case 4: - qb = qb.useIndex("byDepartment"); - qb = ascending ? qb.sort(Sort.asc("department")) : qb.sort(Sort.desc("department")); - query = qb.build(); - break; - case 5: - qb = qb.useIndex("byUserGroup"); - qb = ascending ? qb.sort(Sort.asc("userGroup")) : qb.sort(Sort.desc("userGroup")); - query = qb.build(); - break; - case 6: - if (ascending) { - qb.skip(0); - } - qb = qb.useIndex("bySecondaryDepartmentsAndRoles"); - qb = ascending ? qb.sort(Sort.asc("secondaryDepartmentsAndRoles")) - : qb.sort(Sort.desc("secondaryDepartmentsAndRoles")); - query = qb.build(); - break; - default: - break; + case -1: + case 2: + qb.useIndex(Collections.singletonList("byEmailUser")) + .addSort(Collections.singletonMap("email", ascending ? "asc" : "desc")); + query = qb.build(); + break; + case 0: + qb.useIndex(Collections.singletonList("byFirstName")) + .addSort(Collections.singletonMap("givenname", ascending ? "asc" : "desc")); + query = qb.build(); + break; + case 1: + qb.useIndex(Collections.singletonList("byLastName")) + .addSort(Collections.singletonMap("lastname", ascending ? "asc" : "desc")); + query = qb.build(); + break; + case 3: + qb.useIndex(Collections.singletonList("byActiveStatus")) + .addSort(Collections.singletonMap("deactivated", ascending ? "asc" : "desc")); + query = qb.build(); + break; + case 4: + qb.useIndex(Collections.singletonList("byDepartment")) + .addSort(Collections.singletonMap("department", ascending ? "asc" : "desc")); + query = qb.build(); + break; + case 5: + qb.useIndex(Collections.singletonList("byUserGroup")) + .addSort(Collections.singletonMap("userGroup", ascending ? "asc" : "desc")); + query = qb.build(); + break; + case 6: + if (ascending) { + qb.skip(0); + } + qb.useIndex(Collections.singletonList("bySecondaryDepartmentsAndRoles")) + .addSort(Collections.singletonMap("secondaryDepartmentsAndRoles", ascending ? "asc" : "desc")); + query = qb.build(); + break; + default: + break; } try { - QueryResult queryResult = getConnector().getQueryResult(query, User.class); - users = queryResult.getDocs(); + users = getConnector().getQueryResult(query, User.class); if (sortColumnNo == 6) { - final Selector selectorSecondaryGroupsAndRoles = and(typeSelector, - emptySecondaryDepartmentsAndRolesSelector); - QueryBuilder emptySecondaryGroupsAndRolesQb = new QueryBuilder(selectorSecondaryGroupsAndRoles); + final Map selectorSecondaryGroupsAndRoles = and(List.of(typeSelector, + emptySecondaryDepartmentsAndRolesSelector)); + PostFindOptions.Builder emptySecondaryGroupsAndRolesQb = getConnector().getQueryBuilder() + .selector(selectorSecondaryGroupsAndRoles); emptySecondaryGroupsAndRolesQb.skip(pageData.getDisplayStart()); if (rowsPerPage != -1) { emptySecondaryGroupsAndRolesQb.limit(rowsPerPage); } - QueryResult queryResultWithoutSorting = getConnector() + List userList = getConnector() .getQueryResult(emptySecondaryGroupsAndRolesQb.build(), User.class); - List userList = queryResultWithoutSorting.getDocs(); if (ascending) { userList.addAll(users); users = userList; diff --git a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationRequestRepository.java b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationRequestRepository.java index ae326e2cc9..b3104eeb9c 100644 --- a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationRequestRepository.java +++ b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationRequestRepository.java @@ -10,13 +10,9 @@ package org.eclipse.sw360.moderation.db; -import static com.cloudant.client.api.query.Expression.eq; -import static com.cloudant.client.api.query.Operation.and; -import static com.cloudant.client.api.query.Operation.or; - -import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -27,27 +23,26 @@ import org.eclipse.sw360.components.summary.ModerationRequestSummary; import org.eclipse.sw360.components.summary.SummaryType; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; -import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.couchdb.SummaryAwareRepository; import org.eclipse.sw360.datahandler.thrift.PaginationData; import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import com.ibm.cloud.cloudant.v1.model.PostFindOptions; import com.ibm.cloud.cloudant.v1.model.PostViewOptions; import com.ibm.cloud.cloudant.v1.model.ViewResult; import com.ibm.cloud.cloudant.v1.model.ViewResultRow; import com.ibm.cloud.sdk.core.service.exception.ServiceResponseException; -import com.cloudant.client.api.query.Expression; -import com.cloudant.client.api.query.PredicateExpression; -import com.cloudant.client.api.query.PredicatedOperation; -import com.cloudant.client.api.query.QueryBuilder; -import com.cloudant.client.api.query.QueryResult; -import com.cloudant.client.api.query.Selector; -import com.cloudant.client.api.query.Sort; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import static org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant.and; +import static org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant.elemMatch; +import static org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant.eq; +import static org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant.exists; +import static org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant.or; + /** * CRUD access for the ModerationRequest class * @@ -101,23 +96,26 @@ public ModerationRequestRepository(DatabaseConnectorCloudant db) { } public List getRequestsByDocumentId(String documentId) { - final Selector typeSelector = eq("type", "moderation"); - final Selector filterByModeratorSelector = eq("documentId", documentId); - final Selector finalSelector = and(typeSelector, filterByModeratorSelector); - QueryBuilder qb = new QueryBuilder(finalSelector); - qb.useIndex("byDocumentId"); - List mrs = getConnector().getQueryResult(qb.build(), ModerationRequest.class).getDocs(); + final Map typeSelector = eq("type", "moderation"); + final Map filterByModeratorSelector = eq("documentId", documentId); + final Map finalSelector = and(List.of(typeSelector, filterByModeratorSelector)); + PostFindOptions qb = getConnector().getQueryBuilder() + .selector(finalSelector) + .useIndex(Collections.singletonList("byDocumentId")) + .build(); + List mrs = getConnector().getQueryResult(qb, ModerationRequest.class); return mrs; } public List getRequestsByModerator(String moderator) { - final Selector typeSelector = eq("type", "moderation"); - final Selector filterByModeratorSelector = PredicatedOperation.elemMatch("moderators", - PredicateExpression.eq(moderator)); - final Selector finalSelector = and(typeSelector, filterByModeratorSelector); - QueryBuilder qb = new QueryBuilder(finalSelector); - qb.useIndex("byModerators"); - List mrs = getConnector().getQueryResult(qb.build(), ModerationRequest.class).getDocs(); + final Map typeSelector = eq("type", "moderation"); + final Map filterByModeratorSelector = elemMatch("moderators", moderator); + final Map finalSelector = and(List.of(typeSelector, filterByModeratorSelector)); + PostFindOptions qb = getConnector().getQueryBuilder() + .selector(finalSelector) + .useIndex(Collections.singletonList("byModerators")) + .build(); + List mrs = getConnector().getQueryResult(qb, ModerationRequest.class); return makeSummaryFromFullDocs(SummaryType.SHORT, mrs); } @@ -125,16 +123,17 @@ public List getRequestsByModeratorWithPaginationNoFilter(Stri final int rowsPerPage = pageData.getRowsPerPage(); final boolean ascending = pageData.isAscending(); final int skip = pageData.getDisplayStart(); - final Selector typeSelector = eq("type", "moderation"); - final Selector filterByModeratorSelector = PredicatedOperation.elemMatch("moderators", - PredicateExpression.eq(moderator)); - final Selector finalSelector = and(typeSelector, filterByModeratorSelector); - QueryBuilder qb = new QueryBuilder(finalSelector); - qb.limit(rowsPerPage); - qb.skip(skip); - qb.useIndex("byDate"); - qb = ascending ? qb.sort(Sort.asc("timestamp")) : qb.sort(Sort.desc("timestamp")); - return getConnector().getQueryResult(qb.build(), ModerationRequest.class).getDocs(); + final Map typeSelector = eq("type", "moderation"); + final Map filterByModeratorSelector = elemMatch("moderators", moderator); + final Map finalSelector = and(List.of(typeSelector, filterByModeratorSelector)); + PostFindOptions qb = getConnector().getQueryBuilder() + .selector(finalSelector) + .limit(rowsPerPage) + .skip(skip) + .useIndex(Collections.singletonList("byDate")) + .addSort(Collections.singletonMap("timestamp", ascending ? "asc" : "desc")) + .build(); + return getConnector().getQueryResult(qb, ModerationRequest.class); } public Map> getRequestsByModerator(String moderator, PaginationData pageData, boolean open) { @@ -159,78 +158,71 @@ private Map> queryViewWithPagination(Str List modReqs = Lists.newArrayList(); final boolean ascending = pageData.isAscending(); final int sortColumnNo = pageData.getSortColumnNumber(); - String query = null; - final Selector typeSelector = eq("type", "moderation"); - final Selector openModerationState = or(eq("moderationState", "PENDING"), eq("moderationState", "INPROGRESS")); - final Selector closedModerationState = or(eq("moderationState", "APPROVED"), eq("moderationState", "REJECTED")); - final Selector filterByModeratorSelector = PredicatedOperation.elemMatch("moderators", - PredicateExpression.eq(moderator)); - final Selector emptyComponentTypeSelector = or(Expression.exists("componentType", false), - eq("componentType", "")); - final Selector commonSelector = and(typeSelector, open ? openModerationState : closedModerationState, - filterByModeratorSelector); - QueryBuilder qb = new QueryBuilder(commonSelector); + PostFindOptions query = null; + final Map typeSelector = eq("type", "moderation"); + final Map openModerationState = or(List.of(eq("moderationState", "PENDING"), eq("moderationState", "INPROGRESS"))); + final Map closedModerationState = or(List.of(eq("moderationState", "APPROVED"), eq("moderationState", "REJECTED"))); + final Map filterByModeratorSelector = elemMatch("moderators", moderator); + final Map emptyComponentTypeSelector = or(List.of(exists("componentType", false), + eq("componentType", ""))); + final Map commonSelector = and(List.of(typeSelector, open ? openModerationState : closedModerationState, + filterByModeratorSelector)); + PostFindOptions.Builder qb = getConnector().getQueryBuilder() + .selector(commonSelector); if(rowsPerPage != -1) { qb.limit(rowsPerPage); } qb.skip(pageData.getDisplayStart()); switch (sortColumnNo) { - case -1: - qb = qb.useIndex("byModerators"); - qb = ascending ? qb.sort(Sort.asc("moderators")) : qb.sort(Sort.desc("moderators")); - query = qb.build(); - break; - case 0: - qb = qb.useIndex("byDate"); - qb = ascending ? qb.sort(Sort.asc("timestamp")) : qb.sort(Sort.desc("timestamp")); - query = qb.build(); - break; - case 1: - qb = qb.useIndex("byComponentType"); - qb = ascending ? qb.sort(Sort.asc("componentType")) : qb.sort(Sort.desc("componentType")); - query = qb.build(); - break; - case 2: - qb = qb.useIndex("byDocumentName"); - qb = ascending ? qb.sort(Sort.asc("documentName")) : qb.sort(Sort.desc("documentName")); - query = qb.build(); - break; - case 3: - qb = qb.useIndex("byUsers"); - qb = ascending ? qb.sort(Sort.asc("requestingUser")) : qb.sort(Sort.desc("requestingUser")); - query = qb.build(); - break; - case 4: - qb = qb.useIndex("byDepartment"); - qb = ascending ? qb.sort(Sort.asc("requestingUserDepartment")) - : qb.sort(Sort.desc("requestingUserDepartment")); - query = qb.build(); - break; - case 5: - qb = qb.useIndex("byModerators"); - qb = ascending ? qb.sort(Sort.asc("moderators")) : qb.sort(Sort.desc("moderators")); - query = qb.build(); - break; - case 6: - qb = qb.useIndex("byModerationState"); - qb = ascending ? qb.sort(Sort.asc("moderationState")) : qb.sort(Sort.desc("moderationState")); - query = qb.build(); - break; - default: - break; + case -1, 5: + qb.useIndex(Collections.singletonList("byModerators")) + .addSort(Collections.singletonMap("moderators", ascending ? "asc" : "desc")); + query = qb.build(); + break; + case 0: + qb.useIndex(Collections.singletonList("byDate")) + .addSort(Collections.singletonMap("timestamp", ascending ? "asc" : "desc")); + query = qb.build(); + break; + case 1: + qb.useIndex(Collections.singletonList("byComponentType")) + .addSort(Collections.singletonMap("componentType", ascending ? "asc" : "desc")); + query = qb.build(); + break; + case 2: + qb.useIndex(Collections.singletonList("byDocumentName")) + .addSort(Collections.singletonMap("documentName", ascending ? "asc" : "desc")); + query = qb.build(); + break; + case 3: + qb.useIndex(Collections.singletonList("byUsers")) + .addSort(Collections.singletonMap("requestingUser", ascending ? "asc" : "desc")); + query = qb.build(); + break; + case 4: + qb.useIndex(Collections.singletonList("byDepartment")) + .addSort(Collections.singletonMap("requestingUserDepartment", ascending ? "asc" : "desc")); + query = qb.build(); + break; + case 6: + qb.useIndex(Collections.singletonList("byModerationState")) + .addSort(Collections.singletonMap("moderationState", ascending ? "asc" : "desc")); + query = qb.build(); + break; + default: + break; } try { - QueryResult queryResult = getConnector().getQueryResult(query, ModerationRequest.class); - modReqs = queryResult.getDocs(); + modReqs = getConnector().getQueryResult(query, ModerationRequest.class); if (1 == sortColumnNo) { - final Selector selectorCompType = and(typeSelector, open ? openModerationState : closedModerationState, - filterByModeratorSelector, emptyComponentTypeSelector); - QueryBuilder emptyCTypeQb = new QueryBuilder(selectorCompType); - emptyCTypeQb.limit(rowsPerPage); - emptyCTypeQb.skip(pageData.getDisplayStart()); - QueryResult queryResultWithoutSorting = getConnector() + final Map selectorCompType = and(List.of(typeSelector, open ? openModerationState : closedModerationState, + filterByModeratorSelector, emptyComponentTypeSelector)); + PostFindOptions.Builder emptyCTypeQb = getConnector().getQueryBuilder() + .selector(selectorCompType) + .limit(rowsPerPage) + .skip(pageData.getDisplayStart()); + List mods = getConnector() .getQueryResult(emptyCTypeQb.build(), ModerationRequest.class); - List mods = queryResultWithoutSorting.getDocs(); if (ascending) { mods.addAll(modReqs); modReqs = mods; @@ -258,12 +250,13 @@ private List prepareKeys(String moderator, boolean ascending) { } public List getRequestsByRequestingUser(String user) { - final Selector typeSelector = eq("type", "moderation"); - final Selector filterByModeratorSelector = eq("requestingUser", user); - final Selector finalSelector = and(typeSelector, filterByModeratorSelector); - QueryBuilder qb = new QueryBuilder(finalSelector); - qb.useIndex("byUsers"); - List mrs = getConnector().getQueryResult(qb.build(), ModerationRequest.class).getDocs(); + final Map typeSelector = eq("type", "moderation"); + final Map filterByModeratorSelector = eq("requestingUser", user); + final Map finalSelector = and(List.of(typeSelector, filterByModeratorSelector)); + PostFindOptions.Builder qb = getConnector().getQueryBuilder() + .selector(finalSelector) + .useIndex(Collections.singletonList("byUsers")); + List mrs = getConnector().getQueryResult(qb.build(), ModerationRequest.class); return makeSummaryFromFullDocs(SummaryType.SHORT, mrs); } @@ -271,19 +264,19 @@ public List getRequestsByRequestingUserWithPagination(String final int rowsPerPage = pageData.getRowsPerPage(); final boolean ascending = pageData.isAscending(); final int skip = pageData.getDisplayStart(); - final Selector typeSelector = eq("type", "moderation"); - final Selector filterByModeratorSelector = eq("requestingUser", user); - final Selector finalSelector = and(typeSelector, filterByModeratorSelector); - QueryBuilder qb = new QueryBuilder(finalSelector); - qb.limit(rowsPerPage); - qb.skip(skip); - qb.useIndex("byUsers"); - qb = ascending ? qb.sort(Sort.asc("timestamp")) : qb.sort(Sort.desc("timestamp")); + final Map typeSelector = eq("type", "moderation"); + final Map filterByModeratorSelector = eq("requestingUser", user); + final Map finalSelector = and(List.of(typeSelector, filterByModeratorSelector)); + PostFindOptions.Builder qb = getConnector().getQueryBuilder() + .selector(finalSelector) + .limit(rowsPerPage) + .skip(skip) + .useIndex(Collections.singletonList("byUsers")) + .addSort(Collections.singletonMap("timestamp", ascending ? "asc" : "desc")); List modReqs = Lists.newArrayList(); try { - QueryResult queryResult = getConnector().getQueryResult(qb.build(), ModerationRequest.class); - modReqs = queryResult.getDocs(); + modReqs = getConnector().getQueryResult(qb.build(), ModerationRequest.class); } catch (Exception e) { log.error("Error getting moderation requests", e); } diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java index e68feae20b..eee3dd749b 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java @@ -27,6 +27,7 @@ import java.io.InputStream; import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -469,8 +470,23 @@ public void createIndex(IndexDefinition indexDefinition, String indexName, } } - public QueryResult getQueryResult(String query, Class type) { - return database.query(query, type); + public PostFindOptions.Builder getQueryBuilder() { + return new PostFindOptions.Builder().db(this.dbName); + } + + public List getQueryResult(PostFindOptions query, Class type) { + List results = new ArrayList<>(); + try { + FindResult result = this.instance.getClient().postFind(query).execute().getResult(); + if (result != null) { + results = result.getDocs().stream() + .map(r -> getPojoFromDocument(r, type)) + .toList(); + } + } catch (ServiceResponseException e) { + log.error("Error getting query result", e); + } + return results; } public Set getDistinctSortedStringKeys(@NotNull Class type, String viewName) { @@ -535,4 +551,57 @@ public boolean contains(String docId) { return this.instance.getClient().headDocument(documentOptions).execute() .getStatusCode() == 200; } + + /** + * Generates an $eq selector for given field with given value. + * @param field Field name + * @param value Value to match + * @return New selector + */ + public static @NotNull Map eq(String field, String value) { + return Collections.singletonMap(field, + Collections.singletonMap("$eq", value)); + } + + /** + * Generates an $exists selector for given field with given value. + * @param field Field name + * @param value Value to check + * @return New selector + */ + public static @NotNull Map exists(String field, boolean value) { + return Collections.singletonMap(field, + Collections.singletonMap("$exists", value)); + } + + /** + * Generates an $and selector for list of selectors. + * @param selectors Selectors to combine + * @return New selector + */ + public static @NotNull Map and(List> selectors) { + return Collections.singletonMap("$and", + selectors); + } + + /** + * Generates an $or selector for list of selectors. + * @param selectors Selectors to combine + * @return New selector + */ + public static @NotNull Map or(List> selectors) { + return Collections.singletonMap("$or", + selectors); + } + + /** + * Generates an $elemMatch selector with and $eq selector for the field and value + * @param field Field name + * @param value Value to match + * @return New selector + */ + public static @NotNull Map elemMatch(String field, String value) { + return Collections.singletonMap("$elemMatch", + eq(field, value)); + } } From d93ccf7ca796add435cf58bcda76ef74ff905419 Mon Sep 17 00:00:00 2001 From: Gaurav Mishra Date: Fri, 16 Aug 2024 19:24:57 +0530 Subject: [PATCH 08/10] fix(nouveau): extend nouveau connector as cloudant Signed-off-by: Gaurav Mishra --- .../db/ComponentSearchHandler.java | 2 +- .../db/ModerationSearchHandler.java | 2 +- .../db/ObligationElementSearchHandler.java | 2 +- .../db/ObligationSearchHandler.java | 2 +- .../datahandler/db/PackageSearchHandler.java | 2 +- .../datahandler/db/ProjectSearchHandler.java | 2 +- .../datahandler/db/ReleaseSearchHandler.java | 2 +- .../datahandler/db/UserSearchHandler.java | 2 +- .../datahandler/db/VendorSearchHandler.java | 2 +- .../document/SpdxDocumentSearchHandler.java | 2 +- ...SpdxDocumentCreationInfoSearchHandler.java | 2 +- .../SpdxPackageInfoSearchHandler.java | 2 +- .../db/AbstractDatabaseSearchHandler.java | 4 +- .../DatabaseConnectorCloudant.java | 9 +- .../NouveauLuceneAwareDatabaseConnector.java | 8 +- .../sw360/datahandler/thrift/ThriftUtils.java | 42 +++++-- .../nouveau/LuceneAwareCouchDbConnector.java | 114 +++++++----------- 17 files changed, 99 insertions(+), 102 deletions(-) diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java index 391a400013..9270cb931f 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java @@ -80,7 +80,7 @@ public class ComponentSearchHandler { public ComponentSearchHandler(Cloudant cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java index 5f33e398f8..03ed267847 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java @@ -65,7 +65,7 @@ public class ModerationSearchHandler { public ModerationSearchHandler(Cloudant client, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java index f4f44c3830..aa69b0a68e 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java @@ -53,7 +53,7 @@ public class ObligationElementSearchHandler { public ObligationElementSearchHandler(Cloudant cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); // Creates the database connector and adds the lucene search view - connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java index 2cc6bda50e..f670f9e1fc 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java @@ -48,7 +48,7 @@ public class ObligationSearchHandler { public ObligationSearchHandler(Cloudant cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); // Creates the database connector and adds the lucene search view - connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java index a35e66d899..f0faadb018 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java @@ -74,7 +74,7 @@ public class PackageSearchHandler { public PackageSearchHandler(Cloudant client, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java index f7168ade9e..4efd86643d 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java @@ -82,7 +82,7 @@ public class ProjectSearchHandler { public ProjectSearchHandler(Cloudant client, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java index f9dece6bb8..2da3977aad 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java @@ -52,7 +52,7 @@ public class ReleaseSearchHandler { public ReleaseSearchHandler(Cloudant cClient, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java index 16214dd9b1..d83e75c6ca 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java @@ -72,7 +72,7 @@ public class UserSearchHandler { public UserSearchHandler(Cloudant client, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); // Creates the database connector and adds the lucene search view - connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java index 000d863725..a1225e7023 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java @@ -54,7 +54,7 @@ public class VendorSearchHandler { public VendorSearchHandler(Cloudant client, String dbName) throws IOException { // Creates the database connector and adds the lucene search view DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java index 63231426bc..b8e7e8e281 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java @@ -42,7 +42,7 @@ public class SpdxDocumentSearchHandler { public SpdxDocumentSearchHandler(Cloudant client, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java index fb8d18eaf8..0b9e2f438f 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java @@ -42,7 +42,7 @@ public class SpdxDocumentCreationInfoSearchHandler { public SpdxDocumentCreationInfoSearchHandler(Cloudant client, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java index 3f0647e2f1..483a9498e1 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java @@ -42,7 +42,7 @@ public class SpdxPackageInfoSearchHandler { public SpdxPackageInfoSearchHandler(Cloudant client, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); - connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); diff --git a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java index 4280121833..c8b1874dd6 100644 --- a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java +++ b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java @@ -85,7 +85,7 @@ public AbstractDatabaseSearchHandler(String dbName) throws IOException { Cloudant client = DatabaseSettings.getConfiguredClient(); DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); // Create the database connector and add the search view to couchDB - connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); @@ -98,7 +98,7 @@ public AbstractDatabaseSearchHandler(String dbName) throws IOException { public AbstractDatabaseSearchHandler(Cloudant client, String dbName) throws IOException { DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); // Create the database connector and add the search view to couchDB - connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); Gson gson = db.getInstance().getGson(); NouveauDesignDocument searchView = new NouveauDesignDocument(); searchView.setId(DDOC_NAME); diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java index eee3dd749b..2a804a19c1 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java @@ -13,6 +13,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import com.ibm.cloud.cloudant.v1.Cloudant; import com.ibm.cloud.cloudant.v1.model.*; import com.ibm.cloud.sdk.core.service.exception.ServiceResponseException; @@ -21,12 +22,12 @@ import org.apache.thrift.TBase; import org.apache.thrift.TFieldIdEnum; import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.eclipse.sw360.datahandler.thrift.ThriftUtils; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; import org.jetbrains.annotations.NotNull; import java.io.InputStream; import java.lang.reflect.Field; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -65,8 +66,7 @@ public String getDbName() { public void update(Object document) { DocumentResult resp; if (document != null) { - if (ThriftUtils.isMapped(document.getClass())) { - AttachmentContent content = (AttachmentContent) document; + if (document instanceof AttachmentContent content) { InputStream in = getAttachment(content.getId(), content.getFilename()); resp = this.updateWithResponse(document); createAttachment(resp.getId(), content.getFilename(), in, content.getContentType()); @@ -533,7 +533,8 @@ public Document getDocumentFromPojo(Object document) { } Document doc = new Document(); Gson gson = this.instance.getGson(); - doc.setProperties(gson.fromJson(gson.toJson(document), Map.class)); + Type t = new TypeToken>() {}.getType(); + doc.setProperties(gson.fromJson(gson.toJson(document), t)); return doc; } diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java index 4ca2196e4d..983527a274 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java @@ -71,11 +71,11 @@ public class NouveauLuceneAwareDatabaseConnector extends LuceneAwareCouchDbConne /** * Constructor using a Database connector */ - public NouveauLuceneAwareDatabaseConnector(@NotNull DatabaseConnectorCloudant db, - String ddoc) throws IOException { - super(db.getInstance().getClient(), ddoc); + public NouveauLuceneAwareDatabaseConnector(@NotNull DatabaseConnectorCloudant dbClient, + String ddoc, String db, Gson gson) throws IOException { + super(dbClient.getInstance().getClient(), ddoc, db, gson); setResultLimit(DatabaseSettings.LUCENE_SEARCH_LIMIT); - this.connector = db; + this.connector = dbClient; } public boolean addDesignDoc(@NotNull NouveauDesignDocument designDocument) { diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java index ee14006202..009d3efe46 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java @@ -14,6 +14,11 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import org.eclipse.sw360.datahandler.common.CustomThriftDeserializer; +import org.eclipse.sw360.datahandler.common.CustomThriftSerializer; import org.eclipse.sw360.datahandler.couchdb.deserializer.UsageDataDeserializer; import org.eclipse.sw360.datahandler.thrift.attachments.*; import org.eclipse.sw360.datahandler.thrift.changelogs.ChangeLogs; @@ -40,6 +45,7 @@ import org.apache.thrift.TFieldIdEnum; import com.ibm.cloud.cloudant.v1.model.Document; +import java.lang.reflect.Type; import java.util.Collection; import java.util.List; import java.util.Map; @@ -97,10 +103,6 @@ public class ThriftUtils { UsageData.class, new UsageDataDeserializer() ); - private static final Map, Class>> THRIFT_WRAPPED = ImmutableMap.of( - AttachmentContent.class, AttachmentContentWrapper.class - ); - public static final ImmutableList IMMUTABLE_OF_COMPONENT = ImmutableList.of( Component._Fields.CREATED_BY, Component._Fields.CREATED_ON); @@ -115,15 +117,13 @@ UsageData.class, new UsageDataDeserializer() Release._Fields.CREATED_BY, Release._Fields.CREATED_ON); + private static Gson gson; + private ThriftUtils() { // Utility class with only static functions } - public static boolean isMapped(Class clazz) { - return THRIFT_WRAPPED.containsKey(clazz); - } - public static , F extends TFieldIdEnum> void copyField(T src, T dest, F field) { if (src.isSet(field)) { dest.setFieldValue(field, src.getFieldValue(field)); @@ -148,7 +148,16 @@ public static , FS extends TFieldIdEnum, D extends TBase< } public static Map getIdMap(Collection in) { - return Maps.uniqueIndex(in, Document::getId); + return Maps.uniqueIndex(in, value -> { + if (value instanceof Document doc) { + return doc.getId(); + } + Document doc = new Document(); + Gson gson = getGson(); + Type t = new TypeToken>() {}.getType(); + doc.setProperties(gson.fromJson(gson.toJson(value), t)); + return doc.getId(); + }); } public static , F extends TFieldIdEnum> Function extractField(final F field) { @@ -170,4 +179,19 @@ public static , F extends TFieldIdEnum, R> Function } }; } + + private static Gson getGson() { + if (gson == null) { + GsonBuilder gsonBuilder = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping(); + for (Class c : THRIFT_CLASSES) { + gsonBuilder.registerTypeAdapter(c, new CustomThriftDeserializer()); + gsonBuilder.registerTypeAdapter(c, new CustomThriftSerializer()); + } + for (Class c : THRIFT_NESTED_CLASSES) { + gsonBuilder.registerTypeAdapter(c, new CustomThriftSerializer()); + } + gson = gsonBuilder.create(); + } + return gson; + } } diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java index 3dedc4797a..76030d1988 100644 --- a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java @@ -9,109 +9,81 @@ */ package org.eclipse.sw360.nouveau; -import com.cloudant.client.internal.DatabaseURIHelper; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; import com.ibm.cloud.cloudant.common.SdkCommon; import com.ibm.cloud.cloudant.v1.Cloudant; -import com.ibm.cloud.cloudant.v1.model.SearchAnalyzeResult; import com.ibm.cloud.sdk.core.http.RequestBuilder; import com.ibm.cloud.sdk.core.http.ResponseConverter; +import com.ibm.cloud.sdk.core.http.ServiceCall; import com.ibm.cloud.sdk.core.util.ResponseConverterUtils; +import com.ibm.cloud.sdk.core.util.Validator; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.util.Iterator; +import java.util.HashMap; import java.util.Map; /** * CouchDB connector which allows us to run the Nouveau queries. */ public class LuceneAwareCouchDbConnector { - public static String DEFAULT_NOUVEAU_PREFIX = "_nouveau"; public static String DEFAULT_DESIGN_PREFIX = "_design"; - private final String lucenePrefix; - private final String ddoc; private final NouveauAwareDatabase database; - public LuceneAwareCouchDbConnector(Cloudant db, String ddoc) throws IOException { - this.lucenePrefix = DEFAULT_NOUVEAU_PREFIX; - this.ddoc = ddoc; - this.database = new NouveauAwareDatabase(db, this.ddoc, this.lucenePrefix); + public LuceneAwareCouchDbConnector(Cloudant dbClient, String ddoc, String db, + Gson gson) { + String lucenePrefix = DEFAULT_NOUVEAU_PREFIX; + this.database = new NouveauAwareDatabase(dbClient, db, ddoc, + lucenePrefix, gson); } - public static class NouveauAwareDatabase { + public static class NouveauAwareDatabase extends Cloudant { + private final String db; private final String ddoc; - private final Cloudant client; private final String lucenePrefix; + private final Gson gson; - protected NouveauAwareDatabase(@NotNull Cloudant dbClient, - String ddoc, String lucenePrefix) { - this.client = dbClient; + public NouveauAwareDatabase(@NotNull Cloudant client, String db, + String ddoc, String lucenePrefix, Gson gson) { + super(client.getName(), client.getAuthenticator()); + this.db = db; this.ddoc = ddoc; this.lucenePrefix = lucenePrefix; + this.gson = gson; } - public T queryNouveau(String index, @NotNull NouveauQuery query, Class classOfT) { -// RequestBuilder builder = RequestBuilder.post( -// RequestBuilder.resolveRequestUrl( -// this.client.getServiceUrl(), -// "/" + ensureDesignId(this.ddoc) + "/" + lucenePrefix + "/" + index -// )); -// Map sdkHeaders = SdkCommon.getSdkHeaders( -// "cloudant", "v1", "postNouveauQuery"); -// Iterator> var4 = sdkHeaders.entrySet().iterator(); -// -// while (var4.hasNext()) { -// Map.Entry header = var4.next(); -// builder.header(new Object[]{header.getKey(), header.getValue()}); -// } -// -// builder.header(new Object[]{"Accept", "application/json"}); -// JsonObject contentJson = this.getGson().fromJson(query.buildQuery(this.getGson()), JsonObject.class); -// builder.bodyJson(contentJson); -// ResponseConverter responseConverter = ResponseConverterUtils.getValue((new TypeToken() { -// }).getType()); -// this.client.createServiceCall(builder.build(), responseConverter); - - - URI uri = (new DatabaseURIHelper(this.getDBUri())) - .path(ensureDesignId(this.ddoc)) - .path(lucenePrefix).path(index).build(); - - InputStream response = null; - - T queryResponse; - try { - response = this.client.executeRequest(CouchDbUtil.createPost(uri, query.buildQuery(this.client.getGson()), "application/json")).responseAsInputStream(); - queryResponse = CouchDbUtil.getResponse(response, classOfT, this.client.getGson()); - } catch (IOException | CouchDbException e) { - throw new RuntimeException(e); - } finally { - CouchDbUtil.close(response); + public ServiceCall queryNouveau(String index, @NotNull NouveauQuery query, + Class ignoredClassOfT) { + Validator.notEmpty(index, "index cannot be empty"); + Validator.notNull(query, "query cannot be null"); + + Map pathParamsMap = new HashMap<>(); + pathParamsMap.put("db", this.db); + pathParamsMap.put("ddoc", ensureDesignId(this.ddoc)); + pathParamsMap.put("nouveauPrefix", this.lucenePrefix); + pathParamsMap.put("index", index); + + RequestBuilder builder = RequestBuilder.post(RequestBuilder.resolveRequestUrl( + this.getServiceUrl(), + "/{db}/{ddoc}/{nouveauPrefix}/{index}", + pathParamsMap)); + Map sdkHeaders = SdkCommon.getSdkHeaders("cloudant", "v1", "postNouveauQuery"); + + for (Map.Entry stringStringEntry : sdkHeaders.entrySet()) { + builder.header(stringStringEntry.getKey(), stringStringEntry.getValue()); } - return queryResponse; - } + builder.header("Accept", "application/json"); + builder.header("Content-Type", "application/json"); -// private Gson getGson() { -// GsonBuilder gsonBuilder = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping(); -// for (Class c : ThriftUtils.THRIFT_CLASSES) { -// gsonBuilder.registerTypeAdapter(c, new CustomThriftDeserializer()); -// gsonBuilder.registerTypeAdapter(c, new CustomThriftSerializer()); -// } -// for (Class c : ThriftUtils.THRIFT_NESTED_CLASSES) { -// gsonBuilder.registerTypeAdapter(c, new CustomThriftSerializer()); -// } -// return gsonBuilder.create(); -// } + builder.bodyContent(query.buildQuery(this.gson), "application/json"); + ResponseConverter responseConverter = ResponseConverterUtils.getValue((new TypeToken() { + }).getType()); + return this.createServiceCall(builder.build(), responseConverter); + } } /** @@ -121,7 +93,7 @@ public T queryNouveau(String index, @NotNull NouveauQuery query, Class cl * @return The result of the query. */ public NouveauResult queryNouveau(String index, @NotNull NouveauQuery query) { - return this.database.queryNouveau(index, query, NouveauResult.class); + return this.database.queryNouveau(index, query, NouveauResult.class).execute().getResult(); } /** From bcb672b5a568acbf39abad385f657efd4dfaaec3 Mon Sep 17 00:00:00 2001 From: Gaurav Mishra Date: Tue, 10 Sep 2024 14:41:01 +0530 Subject: [PATCH 09/10] fix(test): fix test cases with cloudant SDK Also migrate some functions from DatabaseConnectorCloudant to LuceneAwareCouchDbConnector which are specific to Lucene Signed-off-by: Gaurav Mishra --- .../db/AttachmentUsageRepository.java | 11 +- .../db/ComponentSearchHandler.java | 2 +- .../db/ModerationSearchHandler.java | 2 +- .../db/ObligationElementSearchHandler.java | 2 +- .../db/ObligationSearchHandler.java | 2 +- .../datahandler/db/PackageSearchHandler.java | 2 +- .../datahandler/db/ProjectSearchHandler.java | 2 +- .../datahandler/db/ReleaseSearchHandler.java | 2 +- .../datahandler/db/UserSearchHandler.java | 2 +- .../datahandler/db/VendorRepository.java | 1 - .../datahandler/db/VendorSearchHandler.java | 2 +- .../document/SpdxDocumentSearchHandler.java | 2 +- ...SpdxDocumentCreationInfoSearchHandler.java | 2 +- .../SpdxPackageInfoSearchHandler.java | 2 +- .../components/db/BulkDeleteUtilTest.java | 9 +- .../db/ComponentDatabaseHandlerTest.java | 3 +- .../db/ComponentSearchHandlerTest.java | 3 +- .../sw360/fossology/FossologyHandler.java | 2 +- .../sw360/health/HealthHandlerTest.java | 6 +- .../sw360/licenses/LicenseHandler.java | 3 - .../testutil/DatabaseTestSetup.java | 3 +- .../sw360/projects/ProjectHandlerTest.java | 3 +- .../eclipse/sw360/search/SearchHandler.java | 2 - .../db/AbstractDatabaseSearchHandler.java | 2 +- .../sw360/search/db/SearchDocument.java | 2 +- .../sw360/vendors/TestVendorClient.java | 3 +- .../sw360/vendors/VendorHandlerTest.java | 5 +- .../vmcomponents/db/VMActionRepository.java | 45 ++- .../db/VMComponentRepository.java | 54 ++-- .../vmcomponents/db/VMMatchRepository.java | 32 +- .../vmcomponents/db/VMPriorityRepository.java | 47 ++- .../db/VMProcessReportingRepository.java | 28 +- .../db/VMDatabaseHandlerTest.java | 5 +- .../handler/SVMSyncHandlerTest.java | 4 +- .../process/VMProcessHandlerTest.java | 2 +- .../db/VulnerabilityRelationRepository.java | 29 +- .../db/VulnerabilityRepository.java | 42 ++- libraries/datahandler/pom.xml | 6 +- .../DatabaseConnectorCloudant.java | 293 +++++++++++++----- .../DatabaseRepositoryCloudantClient.java | 33 +- .../sw360/datahandler/common/CommonUtils.java | 1 - .../couchdb/AttachmentConnector.java | 2 +- .../couchdb/DatabaseNestedMixIn.java | 4 +- .../NouveauLuceneAwareDatabaseConnector.java | 27 +- .../sw360/datahandler/thrift/ThriftUtils.java | 12 +- .../eclipse/sw360/datahandler/TestUtils.java | 10 +- .../common/DatabaseSettingsTest.java | 9 +- .../AttachmentContentDownloaderTest.java | 5 +- .../couchdb/AttachmentContentWrapperTest.java | 31 -- .../AttachmentStreamConnectorTest.java | 27 +- .../couchdb/DatabaseConnectorTest.java | 70 +++-- .../couchdb/DatabaseTestProperties.java | 62 ---- .../couchdb/DocumentWrapperTest.java | 62 ---- .../couchdb/MapperFactoryTest.java | 102 ------ .../ComponentAndAttachmentAwareDBTest.java | 2 +- libraries/nouveau-handler/pom.xml | 5 - .../nouveau/LuceneAwareCouchDbConnector.java | 117 ++++++- .../LuceneAwareCouchDbConnectorTest.java | 4 +- pom.xml | 7 - .../client/persistence/OAuthClientEntity.java | 28 +- .../persistence/OAuthClientRepository.java | 80 ++--- .../client/rest/OAuthClientController.java | 2 +- .../client/rest/OAuthClientResource.java | 37 +-- .../OAuthClientRepositoryTest.java | 2 +- .../SW360RestHealthIndicator.java | 18 +- .../core/RestControllerHelper.java | 10 +- .../Sw360VulnerabilityService.java | 2 +- .../SW360RestHealthIndicatorTest.java | 14 +- .../restdocs/DatabaseSanitationSpecTest.java | 19 +- 69 files changed, 776 insertions(+), 697 deletions(-) delete mode 100644 libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentContentWrapperTest.java delete mode 100644 libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/DatabaseTestProperties.java delete mode 100644 libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/DocumentWrapperTest.java delete mode 100644 libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/MapperFactoryTest.java diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java index 7efdbf7f04..b5afc642c5 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/AttachmentUsageRepository.java @@ -17,6 +17,7 @@ import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import com.ibm.cloud.cloudant.v1.model.PostViewOptions; import com.ibm.cloud.cloudant.v1.model.ViewResult; +import org.apache.commons.lang3.StringUtils; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentUsage; @@ -67,7 +68,7 @@ public List getUsageForAttachment(String ownerId, String attach .getPostViewQueryBuilder(AttachmentUsage.class, "usagesByAttachment") .includeDocs(true) .reduce(false) - .keys(List.of(new String[] { ownerId, attachmentContentId })) + .keys(List.of(List.of(ownerId, attachmentContentId))) .build(); return queryView(viewQuery); } @@ -97,7 +98,7 @@ public List getUsageForAttachment(String ownerId, String attach .getPostViewQueryBuilder(AttachmentUsage.class, "usagesByAttachmentUsageType") .includeDocs(true) .reduce(false) - .keys(List.of(new String[] { ownerId, attachmentContentId, filter })) + .keys(List.of(List.of(ownerId, attachmentContentId, filter))) .build(); return queryView(viewQuery); } @@ -107,7 +108,7 @@ public List getUsedAttachments(String usedById, String filter) .getPostViewQueryBuilder(AttachmentUsage.class, "usedAttachmentsUsageType") .includeDocs(true) .reduce(false) - .keys(List.of(new String[] { usedById, filter })) + .keys(List.of(List.of(usedById, filter))) .build(); return queryView(viewQuery); } @@ -125,9 +126,9 @@ public Map, Integer> getAttachmentUsageCount(Map { String json = key.getKey().toString(); String replace = json.replace("[", "").replace("]", "").replaceAll("\"", ""); - List relIdAttachmentToUsageType = new ArrayList<>(Arrays.asList(replace.split(","))); + List relIdAttachmentToUsageType = Arrays.stream(StringUtils.stripAll(replace.split(","))).toList(); return ImmutableMap.of(relIdAttachmentToUsageType.get(0), relIdAttachmentToUsageType.get(1)); - }, val -> ((Double) val.getValue()).intValue())); + }, val -> (Double.valueOf(val.getValue().toString())).intValue())); } public List getUsageForAttachments(Map> attachments, String filter) { diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java index 9270cb931f..b3ed808277 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java @@ -42,7 +42,7 @@ public class ComponentSearchHandler { private static final Logger log = LogManager.getLogger(ComponentSearchHandler.class); - private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "lucene"; private static final NouveauIndexDesignDocument luceneSearchView = new NouveauIndexDesignDocument("components", diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java index 03ed267847..005347f6e5 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ModerationSearchHandler.java @@ -28,7 +28,7 @@ public class ModerationSearchHandler { - private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "lucene"; private static final NouveauIndexDesignDocument luceneSearchView = new NouveauIndexDesignDocument("moderations", diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java index aa69b0a68e..971ab0ca20 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationElementSearchHandler.java @@ -29,7 +29,7 @@ public class ObligationElementSearchHandler { - private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "lucene"; private static final NouveauIndexDesignDocument luceneSearchView = new NouveauIndexDesignDocument("obligationelements", diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java index f670f9e1fc..f03433b1de 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationSearchHandler.java @@ -28,7 +28,7 @@ public class ObligationSearchHandler { - private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "lucene"; private static final NouveauIndexDesignDocument luceneSearchView = new NouveauIndexDesignDocument("obligations", diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java index f0faadb018..4bcd131234 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java @@ -29,7 +29,7 @@ public class PackageSearchHandler { - private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "lucene"; private static final NouveauIndexDesignDocument luceneSearchView = new NouveauIndexDesignDocument("packages", diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java index 4efd86643d..2e75d3926d 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java @@ -35,7 +35,7 @@ public class ProjectSearchHandler { - private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "lucene"; private static final NouveauIndexDesignDocument luceneSearchView = new NouveauIndexDesignDocument("projects", diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java index 2da3977aad..8a3bbdbc5a 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java @@ -31,7 +31,7 @@ */ public class ReleaseSearchHandler { - private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "lucene"; private static final NouveauIndexDesignDocument luceneSearchView = new NouveauIndexDesignDocument("releases", diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java index d83e75c6ca..4a034f0e57 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/UserSearchHandler.java @@ -28,7 +28,7 @@ public class UserSearchHandler { - private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "lucene"; private static final NouveauIndexDesignDocument luceneSearchView = new NouveauIndexDesignDocument("users", diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorRepository.java index 8a399fb26c..dfee575e67 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorRepository.java @@ -15,7 +15,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java index a1225e7023..6ce9d76f87 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorSearchHandler.java @@ -33,7 +33,7 @@ */ public class VendorSearchHandler { - private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "lucene"; private static final NouveauIndexDesignDocument luceneSearchView = new NouveauIndexDesignDocument("vendors", diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java index b8e7e8e281..afe223335e 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java @@ -27,7 +27,7 @@ public class SpdxDocumentSearchHandler { - private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "lucene"; private static final NouveauIndexDesignDocument luceneSearchView = new NouveauIndexDesignDocument("SPDXDocument", diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java index 0b9e2f438f..fd55f91122 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java @@ -27,7 +27,7 @@ public class SpdxDocumentCreationInfoSearchHandler { - private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "lucene"; private static final NouveauIndexDesignDocument luceneSearchView = new NouveauIndexDesignDocument("documentCreationInformation", diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java index 483a9498e1..cf586fe9b2 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java @@ -27,7 +27,7 @@ public class SpdxPackageInfoSearchHandler { - private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "lucene"; private static final NouveauIndexDesignDocument luceneSearchView = new NouveauIndexDesignDocument("packageInformation", diff --git a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/BulkDeleteUtilTest.java b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/BulkDeleteUtilTest.java index b7c01a76de..9c52b53aa9 100644 --- a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/BulkDeleteUtilTest.java +++ b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/BulkDeleteUtilTest.java @@ -12,6 +12,7 @@ import org.eclipse.sw360.common.utils.BackendUtils; import org.eclipse.sw360.datahandler.TestUtils; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; import org.eclipse.sw360.datahandler.db.ComponentDatabaseHandler; @@ -96,8 +97,8 @@ public class BulkDeleteUtilTest { private Map vendors; private ComponentDatabaseHandler handler; - private DatabaseConnector databaseConnector; - private DatabaseConnector changeLogsDatabaseConnector; + private DatabaseConnectorCloudant databaseConnector; + private DatabaseConnectorCloudant changeLogsDatabaseConnector; private BulkDeleteUtil bulkDeleteUtil; private int nextReleaseVersion = 0; @@ -126,8 +127,8 @@ public void setUp() throws Exception { TestUtils.createDatabase(DatabaseSettingsTest.getConfiguredClient(), changeLogsDbName); // Prepare the database - databaseConnector = new DatabaseConnector(DatabaseSettingsTest.getConfiguredHttpClient(), dbName); - changeLogsDatabaseConnector = new DatabaseConnector(DatabaseSettingsTest.getConfiguredHttpClient(), changeLogsDbName); + databaseConnector = new DatabaseConnectorCloudant(DatabaseSettingsTest.getConfiguredClient(), dbName); + changeLogsDatabaseConnector = new DatabaseConnectorCloudant(DatabaseSettingsTest.getConfiguredClient(), changeLogsDbName); // Prepare vendors for (Vendor vendor : vendors.values()) { diff --git a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentDatabaseHandlerTest.java b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentDatabaseHandlerTest.java index 9b265207de..5920c59018 100644 --- a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentDatabaseHandlerTest.java +++ b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentDatabaseHandlerTest.java @@ -15,6 +15,7 @@ import org.eclipse.sw360.common.utils.BackendUtils; import org.eclipse.sw360.datahandler.TestUtils; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.SW360Constants; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; import org.eclipse.sw360.datahandler.db.ComponentDatabaseHandler; @@ -140,7 +141,7 @@ public void setUp() throws Exception { TestUtils.createDatabase(DatabaseSettingsTest.getConfiguredClient(), dbName); // Prepare the database - DatabaseConnector databaseConnector = new DatabaseConnector(DatabaseSettingsTest.getConfiguredHttpClient(), dbName); + DatabaseConnectorCloudant databaseConnector = new DatabaseConnectorCloudant(DatabaseSettingsTest.getConfiguredClient(), dbName); for (Vendor vendor : vendors.values()) { databaseConnector.add(vendor); diff --git a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentSearchHandlerTest.java b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentSearchHandlerTest.java index f395a521f6..5db44dbe5c 100644 --- a/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentSearchHandlerTest.java +++ b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/ComponentSearchHandlerTest.java @@ -12,6 +12,7 @@ import com.google.common.collect.ImmutableSet; import org.eclipse.sw360.datahandler.TestUtils; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; import org.eclipse.sw360.datahandler.db.ComponentSearchHandler; import org.eclipse.sw360.datahandler.thrift.ThriftClients; @@ -75,7 +76,7 @@ public void setUp() throws Exception { TestUtils.createDatabase(DatabaseSettingsTest.getConfiguredClient(), dbName); // Prepare the database - DatabaseConnector databaseConnector = new DatabaseConnector(DatabaseSettingsTest.getConfiguredHttpClient(), dbName); + DatabaseConnectorCloudant databaseConnector = new DatabaseConnectorCloudant(DatabaseSettingsTest.getConfiguredClient(), dbName); for (Component component : components) { databaseConnector.add(component); diff --git a/backend/src/src-fossology/src/main/java/org/eclipse/sw360/fossology/FossologyHandler.java b/backend/src/src-fossology/src/main/java/org/eclipse/sw360/fossology/FossologyHandler.java index cd796555ec..e4eab7da6a 100644 --- a/backend/src/src-fossology/src/main/java/org/eclipse/sw360/fossology/FossologyHandler.java +++ b/backend/src/src-fossology/src/main/java/org/eclipse/sw360/fossology/FossologyHandler.java @@ -176,7 +176,7 @@ private void updateFossologyProcessInRelease(ExternalToolProcess fossologyProces // because our workflow in between might have taken some time, we have to // refetch the release to get the current version (as another thread might have // written changes to the release which results in a new version so that we - // would get a org.ektorp.UpdateConflictException on trying to write) + // would get a conflict error on trying to write) release = componentClient.getReleaseById(release.getId(), user); Iterator oldFossologyProcessIterator = SW360Utils .getNotOutdatedExternalToolProcessesForTool(release, ExternalTool.FOSSOLOGY).iterator(); diff --git a/backend/src/src-health/src/test/java/org/eclipse/sw360/health/HealthHandlerTest.java b/backend/src/src-health/src/test/java/org/eclipse/sw360/health/HealthHandlerTest.java index 7420d37f12..a7c2612f9d 100644 --- a/backend/src/src-health/src/test/java/org/eclipse/sw360/health/HealthHandlerTest.java +++ b/backend/src/src-health/src/test/java/org/eclipse/sw360/health/HealthHandlerTest.java @@ -33,7 +33,7 @@ public class HealthHandlerTest { @Test public void testGetHealthFailsUponMissingDB() throws MalformedURLException { TestUtils.deleteAllDatabases(); - HealthHandler healthHandler = new HealthHandler(DatabaseSettingsTest.getConfiguredHttpClient()); + HealthHandler healthHandler = new HealthHandler(DatabaseSettingsTest.getConfiguredClient()); final Health health = healthHandler.getHealthOfSpecificDbs(DATABASES_TO_CHECK); assertEquals(Status.DOWN, health.status); assertEquals(DATABASES_TO_CHECK.size(), health.getDetails().size()); @@ -45,7 +45,7 @@ public void testGetHealth() throws MalformedURLException { TestUtils.createDatabase(DatabaseSettingsTest.getConfiguredClient(), database); } - HealthHandler healthHandler = new HealthHandler(DatabaseSettingsTest.getConfiguredHttpClient()); + HealthHandler healthHandler = new HealthHandler(DatabaseSettingsTest.getConfiguredClient()); final Health health = healthHandler.getHealthOfSpecificDbs(DATABASES_TO_CHECK); assertEquals(Status.UP, health.status); assertEquals(new HashMap<>(), health.getDetails()); @@ -60,7 +60,7 @@ public void testGetHealthWithPartialDBMissing() throws MalformedURLException { final String couchDbDatabase = DatabaseSettingsTest.COUCH_DB_DATABASE; TestUtils.createDatabase(DatabaseSettingsTest.getConfiguredClient(), couchDbDatabase); - HealthHandler healthHandler = new HealthHandler(DatabaseSettingsTest.getConfiguredHttpClient()); + HealthHandler healthHandler = new HealthHandler(DatabaseSettingsTest.getConfiguredClient()); final Health health = healthHandler.getHealthOfSpecificDbs(DATABASES_TO_CHECK); assertEquals(Status.ERROR, health.getStatus()); assertEquals(DATABASES_TO_CHECK.size() -1, health.getDetails().size()); diff --git a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/LicenseHandler.java b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/LicenseHandler.java index 079a478e4a..e65b316cb7 100644 --- a/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/LicenseHandler.java +++ b/backend/src/src-licenses/src/main/java/org/eclipse/sw360/licenses/LicenseHandler.java @@ -25,14 +25,11 @@ import org.eclipse.sw360.licenses.db.LicenseDatabaseHandler; import org.eclipse.sw360.datahandler.db.ObligationElementSearchHandler; -import com.cloudant.client.api.CloudantClient; - import org.apache.thrift.TException; import java.nio.ByteBuffer; import java.util.List; import java.util.Set; -import java.util.function.Supplier; import java.io.IOException; import static org.eclipse.sw360.datahandler.common.SW360Assert.*; diff --git a/backend/src/src-moderation/src/test/java/org/eclipse/sw360/moderation/testutil/DatabaseTestSetup.java b/backend/src/src-moderation/src/test/java/org/eclipse/sw360/moderation/testutil/DatabaseTestSetup.java index e8eb71b767..0511aabec1 100644 --- a/backend/src/src-moderation/src/test/java/org/eclipse/sw360/moderation/testutil/DatabaseTestSetup.java +++ b/backend/src/src-moderation/src/test/java/org/eclipse/sw360/moderation/testutil/DatabaseTestSetup.java @@ -10,6 +10,7 @@ package org.eclipse.sw360.moderation.testutil; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; import org.eclipse.sw360.datahandler.thrift.moderation.DocumentType; import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; @@ -25,7 +26,7 @@ public class DatabaseTestSetup { public static void main(String[] args) throws MalformedURLException { - DatabaseConnector db = new DatabaseConnector(DatabaseSettingsTest.getConfiguredHttpClient(), DatabaseSettingsTest.COUCH_DB_DATABASE); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.COUCH_DB_DATABASE); Project project = new Project().setName("Test Project"); project.addToModerators("user1"); diff --git a/backend/src/src-projects/src/test/java/org/eclipse/sw360/projects/ProjectHandlerTest.java b/backend/src/src-projects/src/test/java/org/eclipse/sw360/projects/ProjectHandlerTest.java index 447756b511..905d1a1a1e 100644 --- a/backend/src/src-projects/src/test/java/org/eclipse/sw360/projects/ProjectHandlerTest.java +++ b/backend/src/src-projects/src/test/java/org/eclipse/sw360/projects/ProjectHandlerTest.java @@ -12,6 +12,7 @@ import com.google.common.collect.ImmutableMap; import org.eclipse.sw360.datahandler.TestUtils; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; import org.eclipse.sw360.datahandler.common.SW360Utils; import org.eclipse.sw360.datahandler.db.AttachmentDatabaseHandler; @@ -68,7 +69,7 @@ public void setUp() throws Exception { TestUtils.createDatabase(DatabaseSettingsTest.getConfiguredClient(), dbName); // Prepare the database - DatabaseConnector databaseConnector = new DatabaseConnector(DatabaseSettingsTest.getConfiguredHttpClient(), dbName); + DatabaseConnectorCloudant databaseConnector = new DatabaseConnectorCloudant(DatabaseSettingsTest.getConfiguredClient(), dbName); for (Project project : projects) { databaseConnector.add(project); } diff --git a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/SearchHandler.java b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/SearchHandler.java index 380b588052..d3703b23ea 100644 --- a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/SearchHandler.java +++ b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/SearchHandler.java @@ -10,7 +10,6 @@ */ package org.eclipse.sw360.search; -import com.cloudant.client.api.CloudantClient; import com.google.common.collect.ImmutableMap; import com.ibm.cloud.cloudant.v1.Cloudant; @@ -33,7 +32,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.stream.Collectors; diff --git a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java index c8b1874dd6..82daa22a7e 100644 --- a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java +++ b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/AbstractDatabaseSearchHandler.java @@ -41,7 +41,7 @@ */ public abstract class AbstractDatabaseSearchHandler { - private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "/lucene"; + private static final String DDOC_NAME = DEFAULT_DESIGN_PREFIX + "lucene"; private static final NouveauIndexDesignDocument luceneSearchView = new NouveauIndexDesignDocument("all", new NouveauIndexFunction( diff --git a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/SearchDocument.java b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/SearchDocument.java index 2bc53f71a6..5411a30a0a 100644 --- a/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/SearchDocument.java +++ b/backend/src/src-search/src/main/java/org/eclipse/sw360/search/db/SearchDocument.java @@ -19,7 +19,7 @@ import static org.eclipse.sw360.search.common.SearchConstants.NAME_MAX_LENGTH; /** - * Helper class to help parse JSON documents from lucene-ektorp + * Helper class to help parse JSON documents from lucene * * @author cedric.bodet@tngtech.com */ diff --git a/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/TestVendorClient.java b/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/TestVendorClient.java index 68ce740b09..b261a4e7fa 100644 --- a/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/TestVendorClient.java +++ b/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/TestVendorClient.java @@ -9,6 +9,7 @@ */ package org.eclipse.sw360.vendors; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; import org.eclipse.sw360.datahandler.thrift.vendors.VendorService; @@ -30,7 +31,7 @@ public class TestVendorClient { @SuppressWarnings("unused") public static void InitDatabase() throws MalformedURLException { - DatabaseConnector databaseConnector = new DatabaseConnector(DatabaseSettingsTest.getConfiguredHttpClient(), DatabaseSettingsTest.COUCH_DB_DATABASE); + DatabaseConnectorCloudant databaseConnector = new DatabaseConnectorCloudant(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.COUCH_DB_DATABASE); databaseConnector.add(new Vendor().setShortname("Microsoft").setFullname("Microsoft Corporation").setUrl("http://www.microsoft.com")); databaseConnector.add(new Vendor().setShortname("Apache").setFullname("The Apache Software Foundation").setUrl("http://www.apache.org")); diff --git a/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/VendorHandlerTest.java b/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/VendorHandlerTest.java index 7b9c27316b..266bf793d5 100644 --- a/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/VendorHandlerTest.java +++ b/backend/src/src-vendors/src/test/java/org/eclipse/sw360/vendors/VendorHandlerTest.java @@ -10,6 +10,7 @@ package org.eclipse.sw360.vendors; import org.eclipse.sw360.datahandler.TestUtils; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; import org.eclipse.sw360.datahandler.thrift.AddDocumentRequestSummary; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; @@ -38,7 +39,7 @@ public void setUp() throws Exception { TestUtils.createDatabase(DatabaseSettingsTest.getConfiguredClient(), dbName); // Prepare the database - DatabaseConnector databaseConnector = new DatabaseConnector(DatabaseSettingsTest.getConfiguredHttpClient(), dbName); + DatabaseConnectorCloudant databaseConnector = new DatabaseConnectorCloudant(DatabaseSettingsTest.getConfiguredClient(), dbName); vendorList = new ArrayList<>(); vendorList.add(new Vendor().setShortname("Microsoft").setFullname("Microsoft Corporation").setUrl("http://www.microsoft.com")); vendorList.add(new Vendor().setShortname("Apache").setFullname("The Apache Software Foundation").setUrl("http://www.apache.org")); @@ -48,7 +49,7 @@ public void setUp() throws Exception { databaseConnector.add(vendor); } - vendorHandler = new VendorHandler(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.getConfiguredHttpClient(), dbName); + vendorHandler = new VendorHandler(DatabaseSettingsTest.getConfiguredClient(), dbName); } @After diff --git a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMActionRepository.java b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMActionRepository.java index 3e8758baac..c0adf0dca2 100644 --- a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMActionRepository.java +++ b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMActionRepository.java @@ -4,11 +4,14 @@ */ package org.eclipse.sw360.vmcomponents.db; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.eclipse.sw360.datahandler.couchdb.DatabaseRepository; import org.eclipse.sw360.datahandler.thrift.vmcomponents.VMAction; -import org.ektorp.support.View; +import java.util.HashMap; +import java.util.Map; import java.util.Set; /** @@ -16,8 +19,13 @@ * * @author stefan.jaeger@evosoft.com */ -@View(name = "all", map = "function(doc) { if (doc.type == 'vmaction') emit(null, doc._id) }") -public class VMActionRepository extends DatabaseRepository { +public class VMActionRepository extends DatabaseRepositoryCloudantClient { + + private static final String ALL = + "function(doc) {" + + " if (doc.type == 'vmaction') " + + " emit(null, doc._id) " + + "}"; private static final String BY_VMID_VIEW = "function(doc) {" + @@ -40,37 +48,42 @@ public class VMActionRepository extends DatabaseRepository { " } " + "}"; - public VMActionRepository(DatabaseConnector db) { - super(VMAction.class, db); + public VMActionRepository(DatabaseConnectorCloudant db) { + super(db, VMAction.class); - initStandardDesignDocument(); + Map views = new HashMap<>(); + views.put("all", createMapReduce(ALL, null)); + views.put("byvmid", createMapReduce(BY_VMID_VIEW, null)); + views.put("bylastupdate", createMapReduce(BY_LAST_UPDATE_VIEW, null)); + views.put("all_vmids", createMapReduce(ALL_VMIDS, null)); + initStandardDesignDocument(views, db); } - @View(name = "byvmid", map = BY_VMID_VIEW) public VMAction getActionByVmid(String vmid) { final Set idList = queryForIdsAsValue("byvmid", vmid); - if (idList != null && idList.size() > 0) + if (idList != null && !idList.isEmpty()) return get(CommonUtils.getFirst(idList)); return null; } - @View(name = "bylastupdate", map = BY_LAST_UPDATE_VIEW) public VMAction getActionByLastUpdate(String lastUpdateDate) { final Set idList; if (lastUpdateDate == null){ - idList = getAllIdsByView("bylastupdate", true); + idList = queryForIdsAsValue(getConnector() + .getPostViewQueryBuilder(VMAction.class, "bylastupdate") + .descending(true) + .build()); } else { idList = queryForIdsAsValue("bylastupdate", lastUpdateDate); } - if (idList != null && idList.size() > 0) + if (idList != null && !idList.isEmpty()) return get(CommonUtils.getFirst(idList)); return null; } - @View(name = "all_vmids", map = ALL_VMIDS) public Set getAllVmids() { - return queryForIdsAsValue(createQuery("all_vmids")); + return queryForIdsAsValue(getConnector() + .getPostViewQueryBuilder(VMAction.class, "all_vmids") + .build()); } - - } diff --git a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMComponentRepository.java b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMComponentRepository.java index ca6942b581..aba6d17cd0 100644 --- a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMComponentRepository.java +++ b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMComponentRepository.java @@ -4,11 +4,14 @@ */ package org.eclipse.sw360.vmcomponents.db; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.eclipse.sw360.datahandler.couchdb.DatabaseRepository; import org.eclipse.sw360.datahandler.thrift.vmcomponents.VMComponent; -import org.ektorp.support.View; +import java.util.HashMap; +import java.util.Map; import java.util.Set; /** @@ -16,8 +19,13 @@ * * @author stefan.jaeger@evosoft.com */ -@View(name = "all", map = "function(doc) { if (doc.type == 'vmcomponent') emit(null, doc._id) }") -public class VMComponentRepository extends DatabaseRepository { +public class VMComponentRepository extends DatabaseRepositoryCloudantClient { + + private static final String ALL = + "function(doc) {" + + " if (doc.type == 'vmcomponent') " + + " emit(null, doc._id) " + + "}"; private static final String BY_VMID_VIEW = "function(doc) {" + @@ -61,51 +69,57 @@ public class VMComponentRepository extends DatabaseRepository { " } " + "}"; - public VMComponentRepository(DatabaseConnector db) { - super(VMComponent.class, db); - - initStandardDesignDocument(); + public VMComponentRepository(DatabaseConnectorCloudant db) { + super(db, VMComponent.class); + + Map views = new HashMap<>(); + views.put("all", createMapReduce(ALL, null)); + views.put("byvmid", createMapReduce(BY_VMID_VIEW, null)); + views.put("bylastupdate", createMapReduce(BY_LAST_UPDATE_VIEW, null)); + views.put("all_vmids", createMapReduce(ALL_VMIDS, null)); + views.put("componentByName", createMapReduce(BY_LOWERCASE_NAME_VIEW, null)); + views.put("componentByVendor", createMapReduce(BY_LOWERCASE_VENDOR_VIEW, null)); + views.put("componentByVersion", createMapReduce(BY_LOWERCASE_VERSION_VIEW, null)); + initStandardDesignDocument(views, db); } - @View(name = "byvmid", map = BY_VMID_VIEW) public VMComponent getComponentByVmid(String vmid) { final Set idList = queryForIdsAsValue("byvmid", vmid); - if (idList != null && idList.size() > 0) + if (idList != null && !idList.isEmpty()) return get(CommonUtils.getFirst(idList)); return null; } - @View(name = "bylastupdate", map = BY_LAST_UPDATE_VIEW) public VMComponent getComponentByLastUpdate(String lastUpdateDate) { final Set idList; - if (lastUpdateDate == null){ - idList = getAllIdsByView("bylastupdate", true); + if (lastUpdateDate == null) { + idList = queryForIdsAsValue(getConnector() + .getPostViewQueryBuilder(VMComponent.class, "bylastupdate") + .descending(true) + .build()); } else { idList = queryForIdsAsValue("bylastupdate", lastUpdateDate); } - if (idList != null && idList.size() > 0) + if (idList != null && !idList.isEmpty()) return get(CommonUtils.getFirst(idList)); return null; } - @View(name = "all_vmids", map = ALL_VMIDS) public Set getAllVmids() { - return queryForIdsAsValue(createQuery("all_vmids")); + return queryForIdsAsValue(getConnector() + .getPostViewQueryBuilder(VMComponent.class, "all_vmids") + .build()); } - @View(name = "componentByName", map = BY_LOWERCASE_NAME_VIEW) public Set getComponentByLowercaseNamePrefix(String namePrefix) { return queryForIdsByPrefix("componentByName", namePrefix != null ? namePrefix.toLowerCase() : namePrefix); } - @View(name = "componentByVendor", map = BY_LOWERCASE_VENDOR_VIEW) public Set getComponentByLowercaseVendorPrefix(String vendorPrefix) { return queryForIdsByPrefix("componentByVendor", vendorPrefix != null ? vendorPrefix.toLowerCase() : vendorPrefix); } - @View(name = "componentByVersion", map = BY_LOWERCASE_VERSION_VIEW) public Set getComponentByLowercaseVersionPrefix(String versionPrefix) { return queryForIdsByPrefix("componentByVersion", versionPrefix != null ? versionPrefix.toLowerCase() : versionPrefix); } - } diff --git a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMMatchRepository.java b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMMatchRepository.java index fabcefc110..c97f4cd6b9 100644 --- a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMMatchRepository.java +++ b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMMatchRepository.java @@ -4,13 +4,16 @@ */ package org.eclipse.sw360.vmcomponents.db; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.vmcomponents.VMMatch; import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.eclipse.sw360.datahandler.couchdb.DatabaseRepository; -import org.ektorp.support.View; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -18,8 +21,13 @@ * * @author stefan.jaeger@evosoft.com */ -@View(name = "all", map = "function(doc) { if (doc.type == 'vmmatch') emit(null, doc._id) }") -public class VMMatchRepository extends DatabaseRepository { +public class VMMatchRepository extends DatabaseRepositoryCloudantClient { + + private static final String ALL = + "function (doc) {" + + " if (doc.type == 'vmmatch')" + + " emit(null, doc._id) " + + "}"; private static final String BY_IDs_VIEW = "function(doc) {" + @@ -35,24 +43,24 @@ public class VMMatchRepository extends DatabaseRepository { " } " + "}"; - public VMMatchRepository(DatabaseConnector db) { - super(VMMatch.class, db); + public VMMatchRepository(DatabaseConnectorCloudant db) { + super(db, VMMatch.class); - initStandardDesignDocument(); + Map views = new HashMap<>(); + views.put("all", createMapReduce(ALL, null)); + views.put("byids", createMapReduce(BY_IDs_VIEW, null)); + views.put("byComponentId", createMapReduce(BY_COMPONENT_ID_VIEW, null)); + initStandardDesignDocument(views, db); } - @View(name = "byids", map = BY_IDs_VIEW) public VMMatch getMatchByIds(String releaseId, String vmComponentId) { final Set idList = queryForIdsAsComplexValue("byids", releaseId, vmComponentId); - if (idList != null && idList.size() > 0) + if (idList != null && !idList.isEmpty()) return get(CommonUtils.getFirst(idList)); return null; } - @View(name = "byComponentId", map = BY_COMPONENT_ID_VIEW) public List getMatchesByComponentIds(Collection componentIds) { return queryByIds("byComponentId", componentIds); } - - } diff --git a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMPriorityRepository.java b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMPriorityRepository.java index 29c73c1540..c250425419 100644 --- a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMPriorityRepository.java +++ b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMPriorityRepository.java @@ -4,11 +4,14 @@ */ package org.eclipse.sw360.vmcomponents.db; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.vmcomponents.VMPriority; import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.eclipse.sw360.datahandler.couchdb.DatabaseRepository; -import org.ektorp.support.View; +import java.util.HashMap; +import java.util.Map; import java.util.Set; /** @@ -16,8 +19,13 @@ * * @author stefan.jaeger@evosoft.com */ -@View(name = "all", map = "function(doc) { if (doc.type == 'vmpriority') emit(null, doc._id) }") -public class VMPriorityRepository extends DatabaseRepository { +public class VMPriorityRepository extends DatabaseRepositoryCloudantClient { + + private static final String ALL = + "function(doc) {" + + " if (doc.type == 'vmpriority')" + + " emit(null, doc._id) " + + "}"; private static final String BY_VMID_VIEW = "function(doc) {" + @@ -40,37 +48,42 @@ public class VMPriorityRepository extends DatabaseRepository { " } " + "}"; - public VMPriorityRepository(DatabaseConnector db) { - super(VMPriority.class, db); + public VMPriorityRepository(DatabaseConnectorCloudant db) { + super(db, VMPriority.class); - initStandardDesignDocument(); + Map views = new HashMap<>(); + views.put("all", createMapReduce(ALL, null)); + views.put("byvmid", createMapReduce(BY_VMID_VIEW, null)); + views.put("bylastupdate", createMapReduce(BY_LAST_UPDATE_VIEW, null)); + views.put("all_vmids", createMapReduce(ALL_VMIDS, null)); + initStandardDesignDocument(views, db); } - @View(name = "byvmid", map = BY_VMID_VIEW) public VMPriority getPriorityByVmid(String vmid) { final Set idList = queryForIdsAsValue("byvmid", vmid); - if (idList != null && idList.size() > 0) + if (idList != null && !idList.isEmpty()) return get(CommonUtils.getFirst(idList)); return null; } - @View(name = "bylastupdate", map = BY_LAST_UPDATE_VIEW) public VMPriority getPriorityByLastUpdate(String lastUpdateDate) { final Set idList; - if (lastUpdateDate == null){ - idList = getAllIdsByView("bylastupdate", true); + if (lastUpdateDate == null) { + idList = queryForIdsAsValue(getConnector() + .getPostViewQueryBuilder(VMPriority.class, "bylastupdate") + .descending(true) + .build()); } else { idList = queryForIdsAsValue("bylastupdate", lastUpdateDate); } - if (idList != null && idList.size() > 0) + if (idList != null && !idList.isEmpty()) return get(CommonUtils.getFirst(idList)); return null; } - @View(name = "all_vmids", map = ALL_VMIDS) public Set getAllVmids() { - return queryForIdsAsValue(createQuery("all_vmids")); + return queryForIdsAsValue(getConnector() + .getPostViewQueryBuilder(VMPriority.class, "all_vmids") + .build()); } - - } diff --git a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMProcessReportingRepository.java b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMProcessReportingRepository.java index 1a71c593af..484376b8c2 100644 --- a/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMProcessReportingRepository.java +++ b/backend/src/src-vmcomponents/src/main/java/org/eclipse/sw360/vmcomponents/db/VMProcessReportingRepository.java @@ -4,11 +4,14 @@ */ package org.eclipse.sw360.vmcomponents.db; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.vmcomponents.VMProcessReporting; import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.eclipse.sw360.datahandler.couchdb.DatabaseRepository; -import org.ektorp.support.View; +import java.util.HashMap; +import java.util.Map; import java.util.Set; /** @@ -16,8 +19,13 @@ * * @author stefan.jaeger@evosoft.com */ -@View(name = "all", map = "function(doc) { if (doc.type == 'vmprocessreporting') emit(null, doc._id) }") -public class VMProcessReportingRepository extends DatabaseRepository { +public class VMProcessReportingRepository extends DatabaseRepositoryCloudantClient { + + private static final String ALL = + "function(doc) {" + + " if (doc.type == 'vmprocessreporting')" + + " emit(null, doc._id) " + + "}"; private static final String BY_START_DATE = "function(doc) {" + @@ -26,16 +34,18 @@ public class VMProcessReportingRepository extends DatabaseRepository views = new HashMap<>(); + views.put("all", createMapReduce(ALL, null)); + views.put("bystartdate", createMapReduce(BY_START_DATE, null)); + initStandardDesignDocument(views, db); } - @View(name = "bystartdate", map = BY_START_DATE) public VMProcessReporting getProcessReportingByStartDate(String startDate) { final Set idList = queryForIdsAsValue("bystartdate", startDate); - if (idList != null && idList.size() > 0) + if (idList != null && !idList.isEmpty()) return get(CommonUtils.getFirst(idList)); return null; } diff --git a/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/db/VMDatabaseHandlerTest.java b/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/db/VMDatabaseHandlerTest.java index 8acb711fe5..37721a3896 100644 --- a/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/db/VMDatabaseHandlerTest.java +++ b/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/db/VMDatabaseHandlerTest.java @@ -6,6 +6,7 @@ package org.eclipse.sw360.vmcomponents.db; import org.eclipse.sw360.datahandler.TestUtils; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; import org.eclipse.sw360.datahandler.common.SW360Utils; import org.eclipse.sw360.datahandler.thrift.RequestStatus; @@ -58,7 +59,7 @@ public void setUp() throws Exception { TestUtils.createDatabase(DatabaseSettingsTest.getConfiguredClient(), dbName); // Prepare the database - DatabaseConnector databaseConnector = new DatabaseConnector(DatabaseSettingsTest.getConfiguredHttpClient(), dbName); + DatabaseConnectorCloudant databaseConnector = new DatabaseConnectorCloudant(DatabaseSettingsTest.getConfiguredClient(), dbName); // set up prios p1 = new VMPriority().setVmid("1").setShortText("one").setLongText("onelong"); @@ -91,7 +92,7 @@ public void setUp() throws Exception { pr1 = new VMProcessReporting(VMAction.class.getSimpleName(), SW360Utils.getCreatedOnTime()); // Prepare the handler - handler = new VMDatabaseHandler(DatabaseSettingsTest.getConfiguredHttpClient(), DatabaseSettingsTest.COUCH_DB_VM); + handler = new VMDatabaseHandler(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.COUCH_DB_VM); } @After diff --git a/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/handler/SVMSyncHandlerTest.java b/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/handler/SVMSyncHandlerTest.java index ecd5b47e46..fb0e19e3db 100644 --- a/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/handler/SVMSyncHandlerTest.java +++ b/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/handler/SVMSyncHandlerTest.java @@ -84,7 +84,7 @@ public void setUp() throws TException, IOException { releaseHandler = new SVMSyncHandler(Release.class); user = new User().setEmail("me"); // Prepare the handler - handler = new VMDatabaseHandler(DatabaseSettingsTest.getConfiguredHttpClient(), DatabaseSettingsTest.COUCH_DB_VM); + handler = new VMDatabaseHandler(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.COUCH_DB_VM); compDBHandler = new ComponentDatabaseHandler(DatabaseSettingsTest.getConfiguredClient(), dbNameComp, dbNameAtt); vendorRepository = new VendorRepository(new DatabaseConnectorCloudant(DatabaseSettingsTest.getConfiguredClient(), dbNameComp)); @@ -102,7 +102,7 @@ public void tearDown() throws Exception { // @Test public void create5000Matches() throws SW360Exception, MalformedURLException { - VMDatabaseHandler handler = new VMDatabaseHandler(DatabaseSettingsTest.getConfiguredHttpClient(), DatabaseSettingsTest.COUCH_DB_VM); + VMDatabaseHandler handler = new VMDatabaseHandler(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.COUCH_DB_VM); ComponentDatabaseHandler compDBHandler = new ComponentDatabaseHandler(DatabaseSettingsTest.getConfiguredClient(), dbNameComp, dbNameAtt); VMComponent component = new VMComponent(SW360Utils.getCreatedOnTime(), "droelf"); component.setName("droelf"); diff --git a/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/process/VMProcessHandlerTest.java b/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/process/VMProcessHandlerTest.java index b76ad2f6f4..8be87824be 100644 --- a/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/process/VMProcessHandlerTest.java +++ b/backend/src/src-vmcomponents/src/test/java/org/eclipse/sw360/vmcomponents/process/VMProcessHandlerTest.java @@ -78,7 +78,7 @@ public void setUp() throws TException, IOException { TestUtils.createDatabase(DatabaseSettingsTest.getConfiguredClient(), dbName); // Prepare the handler - handler = new VMDatabaseHandler(DatabaseSettingsTest.getConfiguredHttpClient(), DatabaseSettingsTest.COUCH_DB_VM); + handler = new VMDatabaseHandler(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.COUCH_DB_VM); // mock preparation staticJSONResponse("/portal/api/v1/public/actions/5", "{\"text\": \"Install New Package\"}"); diff --git a/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityRelationRepository.java b/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityRelationRepository.java index 49b4f9bf15..3955c884cd 100644 --- a/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityRelationRepository.java +++ b/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityRelationRepository.java @@ -9,14 +9,16 @@ */ package org.eclipse.sw360.vulnerabilities.db; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.eclipse.sw360.datahandler.couchdb.DatabaseRepository; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.ReleaseVulnerabilityRelation; -import org.ektorp.support.View; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -24,8 +26,13 @@ * * @author stefan.jaeger@evosoft.com */ -@View(name = "all", map = "function(doc) { if (doc.type == 'releasevulnerabilityrelation') emit(null, doc._id) }") -public class VulnerabilityRelationRepository extends DatabaseRepository { +public class VulnerabilityRelationRepository extends DatabaseRepositoryCloudantClient { + + private static final String ALL = + "function(doc) {" + + " if (doc.type == 'releasevulnerabilityrelation')" + + " emit(null, doc._id) " + + "}"; private static final String BY_IDs_VIEW = "function(doc) {" + @@ -49,25 +56,27 @@ public class VulnerabilityRelationRepository extends DatabaseRepository views = new HashMap<>(); + views.put("all", createMapReduce(ALL, null)); + views.put("byids", createMapReduce(BY_IDs_VIEW, null)); + views.put("byReleaseId", createMapReduce(BY_RELEASE_ID_VIEW, null)); + views.put("byVulnerabilityId", createMapReduce(BY_VULNERABILITY_ID_VIEW, null)); + initStandardDesignDocument(views, db); } - @View(name = "byids", map = BY_IDs_VIEW) public ReleaseVulnerabilityRelation getRelationByIds(String releaseId, String vulnerabilityId) { final Set idList = queryForIdsAsComplexValue("byids", releaseId, vulnerabilityId); - if (idList != null && idList.size() > 0) + if (idList != null && !idList.isEmpty()) return get(CommonUtils.getFirst(idList)); return null; } - @View(name = "byReleaseId", map = BY_RELEASE_ID_VIEW) public List getRelationsByReleaseIds(Collection releaseIds) { return queryByIds("byReleaseId", releaseIds); } - @View(name = "byVulnerabilityId", map = BY_VULNERABILITY_ID_VIEW) public List getRelationsByVulnerabilityId(String vulnerabilityId) { return queryView("byVulnerabilityId", vulnerabilityId); } diff --git a/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityRepository.java b/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityRepository.java index 02a8b3b98e..45011e86f0 100644 --- a/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityRepository.java +++ b/backend/src/src-vulnerabilities/src/main/java/org/eclipse/sw360/vulnerabilities/db/VulnerabilityRepository.java @@ -9,13 +9,15 @@ */ package org.eclipse.sw360.vulnerabilities.db; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.eclipse.sw360.datahandler.couchdb.DatabaseRepository; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.Vulnerability; -import org.ektorp.support.View; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -23,8 +25,13 @@ * * @author stefan.jaeger@evosoft.com */ -@View(name = "all", map = "function(doc) { if (doc.type == 'vulnerability') emit(null, doc._id) }") -public class VulnerabilityRepository extends DatabaseRepository { +public class VulnerabilityRepository extends DatabaseRepositoryCloudantClient { + + private static final String ALL = + "function(doc) {" + + " if (doc.type == 'vulnerability')" + + " emit(null, doc._id) " + + "}"; private static final String BY_EXTERNALID_VIEW = "function(doc) {" + @@ -57,22 +64,30 @@ public class VulnerabilityRepository extends DatabaseRepository { "}"; public VulnerabilityRepository(DatabaseConnectorCloudant db) { - super(Vulnerability.class, db); + super(db, Vulnerability.class); - initStandardDesignDocument(); + Map views = new HashMap<>(); + views.put("all", createMapReduce(ALL, null)); + views.put("byexternalid", createMapReduce(BY_EXTERNALID_VIEW, null)); + views.put("bylastupdate", createMapReduce(BY_LAST_UPDATE_VIEW, null)); + views.put("byvunerableconfig", createMapReduce(BY_VULNERABLE_CONFIGURATION, null)); + views.put("all_externalids", createMapReduce(ALL_EXTERNALIDS, null)); + initStandardDesignDocument(views, db); } - @View(name = "byexternalid", map = BY_EXTERNALID_VIEW) public Vulnerability getVulnerabilityByExternalid(String externalid) { final Set idList = queryForIdsAsValue("byexternalid", externalid); - if (idList != null && idList.size() > 0) + if (idList != null && !idList.isEmpty()) return get(CommonUtils.getFirst(idList)); return null; } - @View(name = "bylastupdate", map = BY_LAST_UPDATE_VIEW) public List getVulnerabilitiesByLastUpdate(int limit) { - List ids = getIdListByView("bylastupdate", true, limit); + Set ids = queryForIdsAsValue(getConnector() + .getPostViewQueryBuilder(Vulnerability.class, "bylastupdate") + .descending(true) + .limit(limit) + .build()); return get(ids); } @@ -91,18 +106,17 @@ public List getVulnerabilitiesByExternalIdOrVulnerableConfig(Stri } } - @View(name = "byexternalid", map = BY_EXTERNALID_VIEW) private Set getIdsByExternalIds(String externalId) { return queryForIdsByPrefix("byexternalid", externalId); } - @View(name = "byvunerableconfig", map = BY_VULNERABLE_CONFIGURATION) private Set getIdsByVulnerableConfig(String vulnerableConfig) { return queryForIdsByPrefix("byvunerableconfig", vulnerableConfig); } - @View(name = "all_externalids", map = ALL_EXTERNALIDS) public Set getAllExternalIds() { - return queryForIdsAsValue(createQuery("all_externalids")); + return queryForIdsAsValue(getConnector() + .getPostViewQueryBuilder(Vulnerability.class, "all_externalids") + .build()); } } diff --git a/libraries/datahandler/pom.xml b/libraries/datahandler/pom.xml index 1c443b05ce..32069a1dd1 100644 --- a/libraries/datahandler/pom.xml +++ b/libraries/datahandler/pom.xml @@ -175,6 +175,7 @@ org.apache.httpcomponents.client5 httpclient5 + ${httpclient5.version} org.apache.logging.log4j @@ -198,11 +199,6 @@ gson ${gson.version} - - com.cloudant - cloudant-client - ${cloudant.version} - com.ibm.cloud cloudant diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java index 2a804a19c1..ad3d705d7a 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseConnectorCloudant.java @@ -16,12 +16,14 @@ import com.google.gson.reflect.TypeToken; import com.ibm.cloud.cloudant.v1.Cloudant; import com.ibm.cloud.cloudant.v1.model.*; +import com.ibm.cloud.sdk.core.service.exception.NotFoundException; import com.ibm.cloud.sdk.core.service.exception.ServiceResponseException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.thrift.TBase; import org.apache.thrift.TFieldIdEnum; import org.eclipse.sw360.datahandler.common.CommonUtils; +import org.eclipse.sw360.datahandler.thrift.SW360Exception; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; import org.jetbrains.annotations.NotNull; @@ -73,13 +75,7 @@ public void update(Object document) { } else { resp = this.updateWithResponse(document); } - if (TBase.class.isAssignableFrom(document.getClass())) { - TBase tbase = (TBase) document; - TFieldIdEnum id = tbase.fieldForId(1); - TFieldIdEnum rev = tbase.fieldForId(2); - tbase.setFieldValue(id, resp.getId()); - tbase.setFieldValue(rev, resp.getRev()); - } + updateIdAndRev(document, resp.getId(), resp.getRev()); } else { log.warn("Ignore updating a null document."); } @@ -125,13 +121,16 @@ public Set getAllIds(@NotNull Class type) { return ids; } + /** + * Get a document from DB and convert to SW360 type. + * @param type Type to translate the document to. + * @param id Document ID + * @return Document of type if found. + * @param Type to translate the document to. + */ public T get(Class type, String id) { try { - GetDocumentOptions documentOption = new GetDocumentOptions.Builder() - .db(this.dbName) - .docId(id) - .build(); - Document doc = this.instance.getClient().getDocument(documentOption).execute().getResult(); + Document doc = getDocument(id); T obj = this.getPojoFromDocument(doc, type); String extractedType = null; @@ -151,9 +150,32 @@ public T get(Class type, String id) { } } return obj; - } catch (Exception e) { - log.error("Error fetching document of type " + type.getSimpleName() + " with id " + id + " : " - + e.getMessage()); + } catch (IllegalAccessException | SW360Exception e) { + log.error("Error fetching document of type {} with id {} : {}", + type.getSimpleName(), id, e.getMessage()); + return null; + } + } + + /** + * Get a design document from DB with dDoc + * @param ddoc ddoc of Design Document + * @return Design Document if found. Null otherwise. + */ + public DesignDocument getDesignDoc(String ddoc) { + try { + GetDesignDocumentOptions designDocumentOptions = new GetDesignDocumentOptions.Builder() + .db(this.dbName) + .ddoc(ddoc) + .latest(true) + .build(); + + return this.getInstance().getClient().getDesignDocument(designDocumentOptions) + .execute() + .getResult(); + } catch (NotFoundException e) { + log.error("Error fetching design document with id _design/{} : {}", + ddoc, e.getMessage()); return null; } } @@ -179,17 +201,15 @@ public List getAll(Class type) { } public boolean remove(String id) { + if (!contains(id)) { + return false; + } DeleteDocumentOptions deleteOption = new DeleteDocumentOptions.Builder() .db(this.dbName) .docId(id) .build(); - DocumentResult resp = this.instance.getClient().deleteDocument(deleteOption).execute().getResult(); - boolean success = resp.isOk(); - if (!success) { - log.error("Could not delete document with id: " + id); - } - return success; + return deleteDocumentWithOption(deleteOption); } public List getDocuments(Collection ids) { @@ -215,17 +235,65 @@ public List getDocuments(Collection ids) { } } - public Document getDocument(String id) { - try { - GetDocumentOptions getDocOption = new GetDocumentOptions.Builder() - .db(this.dbName) - .docId(id) - .build(); + /** + * Get Cloudant Document from DB for give ID. Will use GET call for most + * request and POST for IDs with `+`. + * @param id Document ID + * @return Document if found. Empty document otherwise. + * @see DatabaseConnectorCloudant::getDocumentWithPost() + */ + public Document getDocument(@NotNull String id) throws SW360Exception { + if (id.contains("+")) { + return getDocumentWithPost(id); + } + return getDocumentWithGet(id); + } + + /** + * Use GET request to get document (with caching) + * @param id Document ID + * @return Document if exists. + * @throws SW360Exception If document is not found for ID + */ + private Document getDocumentWithGet(String id) throws SW360Exception { + GetDocumentOptions getDocOption = new GetDocumentOptions.Builder() + .db(this.dbName) + .docId(id) + .build(); + try { return this.instance.getClient().getDocument(getDocOption).execute().getResult(); - } catch (ServiceResponseException e) { - log.error("Error fetching document", e); - return new Document(); + } catch (NotFoundException e) { + throw new SW360Exception("Cannot find document: " + id); + } + } + + /** + * Get document with `+` in id with POST call. Known issue with CloudantSDK + * https://github.com/IBM/cloudant-java-sdk/blob/51b7da64dea925dc1dd0b2a980dba93e0c899297/KNOWN_ISSUES.md#path-elements-containing-the--character + * @param id Document ID + * @return Document if found. + * @throws SW360Exception If document is not found for ID + */ + private Document getDocumentWithPost(String id) throws SW360Exception { + List idList = List.of(id); + + PostAllDocsOptions postDocsOption = new PostAllDocsOptions.Builder() + .db(this.dbName) + .keys(idList) + .includeDocs(true) + .build(); + + try { + AllDocsResult resp = this.instance.getClient().postAllDocs(postDocsOption).execute().getResult(); + + if (resp.getRows().isEmpty() || resp.getRows().get(0).getDoc() == null) { + return new Document(); + } + + return resp.getRows().get(0).getDoc(); + } catch (NotFoundException e) { + throw new SW360Exception("Cannot find document: " + id); } } @@ -243,9 +311,12 @@ public List get(Class type, Collection ids) { AllDocsResult resp = this.instance.getClient().postAllDocs(postDocsOption).execute().getResult(); - return resp.getRows().stream().map( - r -> this.getPojoFromDocument(r.getDoc(), type) - ).filter(Objects::nonNull).collect(Collectors.toList()); + return resp.getRows().stream().map(r -> { + if (r.getError() != null && r.getDoc() == null) { + return null; + } + return this.getPojoFromDocument(r.getDoc(), type); + }).filter(Objects::nonNull).collect(Collectors.toList()); } catch (ServiceResponseException e) { log.error("Error fetching documents", e); return Collections.emptyList(); @@ -272,13 +343,7 @@ public List executeBulk(@NotNull Collection list) { try { responses = this.instance.getClient().postBulkDocs(bulkDocsOptions).execute().getResult(); for (int i = 0; i < entities.length; i++) { - if (TBase.class.isAssignableFrom(entities[i].getClass())) { - TBase tbase = (TBase) entities[i]; - TFieldIdEnum id = tbase.fieldForId(1); - TFieldIdEnum rev = tbase.fieldForId(2); - tbase.setFieldValue(id, responses.get(i).getId()); - tbase.setFieldValue(rev, responses.get(i).getRev()); - } + updateIdAndRev(entities[i], responses.get(i).getId(), responses.get(i).getRev()); } } catch (Exception e) { log.error("Error in bulk execution", e); @@ -400,33 +465,54 @@ public void createAttachment(String attachmentContentId, String fileName, } public boolean deleteById(String id) { - if (this.contains(id)) { - Document doc = getDocument(id); + if (!contains(id)) { + return false; + } - DeleteDocumentOptions deleteOption = new DeleteDocumentOptions.Builder() - .db(this.dbName) - .docId(id) - .rev(doc.getRev()) - .build(); - return this.instance.getClient().deleteDocument(deleteOption).execute().getResult().isOk(); + Document doc; + try { + doc = getDocument(id); + } catch (SW360Exception e) { + return false; } - return true; + DeleteDocumentOptions deleteOption = new DeleteDocumentOptions.Builder() + .db(this.dbName) + .docId(id) + .rev(doc.getRev()) + .build(); + + return deleteDocumentWithOption(deleteOption); + } + + private boolean deleteDocumentWithOption(DeleteDocumentOptions deleteOption) { + DocumentResult resp; + boolean success; + try { + resp = this.instance.getClient().deleteDocument(deleteOption).execute().getResult(); + success = resp.isOk(); + } catch (ServiceResponseException e) { + log.error("Error deleting document with id {}", deleteOption.docId(), e); + success = false; + } + if (!success) { + log.error("Could not delete document with id: {}", deleteOption.docId()); + } + return success; } public boolean add(T doc) { + Document document = this.getDocumentFromPojo(doc); + if (document.getId() != null && this.contains(document.getId())) { + // Cannot add same document again. Must update. + return false; + } PostDocumentOptions postDocOption = new PostDocumentOptions.Builder() .db(this.dbName) - .document(this.getDocumentFromPojo(doc)) + .document(document) .build(); DocumentResult resp = this.instance.getClient().postDocument(postDocOption).execute().getResult(); - if (TBase.class.isAssignableFrom(doc.getClass())) { - TBase tbase = (TBase) doc; - TFieldIdEnum id = tbase.fieldForId(1); - TFieldIdEnum rev = tbase.fieldForId(2); - tbase.setFieldValue(id, resp.getId()); - tbase.setFieldValue(rev, resp.getRev()); - } + updateIdAndRev(doc, resp.getId(), resp.getRev()); return resp.isOk(); } @@ -434,26 +520,49 @@ public boolean remove(T doc) { return this.remove(this.getDocumentFromPojo(doc).getId()); } - public boolean putDesignDocument(DesignDocument designDocument, String docId) { + public boolean putDesignDocument(DesignDocument designDocument, String ddoc) { + DesignDocument existingDoc = getDesignDocument(ddoc); + if (existingDoc != null) { + designDocument.setId(existingDoc.getId()); + designDocument.setRev(existingDoc.getRev()); + } PutDesignDocumentOptions designDocumentOptions = - new PutDesignDocumentOptions.Builder() - .db(this.dbName) - .designDocument(designDocument) - .ddoc(docId) - .build(); + new PutDesignDocumentOptions.Builder() + .db(this.dbName) + .designDocument(designDocument) + .ddoc(ddoc) + .build(); DocumentResult response = - this.instance.getClient() - .putDesignDocument(designDocumentOptions).execute() - .getResult(); + this.instance.getClient() + .putDesignDocument(designDocumentOptions).execute() + .getResult(); boolean success = response.isOk(); if (!success) { log.error("Unable to put design document {} to {}. Error: {}", - designDocument.getId(), docId, response.getError()); + designDocument.getId(), ddoc, response.getError()); + } else { + designDocument.setId(response.getId()); + designDocument.setRev(response.getRev()); } return success; } + public DesignDocument getDesignDocument(String ddoc) { + GetDesignDocumentOptions designDocumentOptions = new GetDesignDocumentOptions.Builder() + .db(this.dbName) + .ddoc(ddoc) + .latest(true) + .build(); + + try { + return this.instance.getClient().getDesignDocument(designDocumentOptions).execute() + .getResult(); + } catch (NotFoundException e) { + return null; + } + } + public void createIndex(IndexDefinition indexDefinition, String indexName, String indexType) { PostIndexOptions indexOptions = new PostIndexOptions.Builder() @@ -534,23 +643,63 @@ public Document getDocumentFromPojo(Object document) { Document doc = new Document(); Gson gson = this.instance.getGson(); Type t = new TypeToken>() {}.getType(); - doc.setProperties(gson.fromJson(gson.toJson(document), t)); + Map map = gson.fromJson(gson.toJson(document), t); + if (map.containsKey("id")) { + doc.setId((String) map.get("id")); + map.remove("id"); + } + if (map.containsKey("_id")) { + doc.setId((String) map.get("_id")); + map.remove("_id"); + } + if (map.containsKey("rev")) { + doc.setRev((String) map.get("rev")); + map.remove("rev"); + } + if (map.containsKey("revision")) { + doc.setRev((String) map.get("revision")); + map.remove("revision"); + } + if (map.containsKey("_rev")) { + doc.setRev((String) map.get("_rev")); + map.remove("_rev"); + } + doc.setProperties(map); return doc; } public T getPojoFromDocument(@NotNull Document document, Class type) { - return this.instance.getGson().fromJson(document.toString(), type); + T doc = this.instance.getGson().fromJson(document.toString(), type); + updateIdAndRev(doc, document.getId(), document.getRev()); + return doc; } - public boolean contains(String docId) { + private void updateIdAndRev(@NotNull T doc, String docId, String docRev) { + if (TBase.class.isAssignableFrom(doc.getClass())) { + TBase tbase = (TBase) doc; + TFieldIdEnum id = tbase.fieldForId(1); + TFieldIdEnum rev = tbase.fieldForId(2); + tbase.setFieldValue(id, docId); + tbase.setFieldValue(rev, docRev); + } + } + + public boolean contains(@NotNull String docId) { + if (docId.isEmpty()) { + return false; + } HeadDocumentOptions documentOptions = new HeadDocumentOptions.Builder() .db(this.dbName) .docId(docId) .build(); - return this.instance.getClient().headDocument(documentOptions).execute() - .getStatusCode() == 200; + try { + return this.instance.getClient().headDocument(documentOptions).execute() + .getStatusCode() == 200; + } catch (NotFoundException e) { + return false; + } } /** diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseRepositoryCloudantClient.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseRepositoryCloudantClient.java index c39fc67da0..807e7b344d 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseRepositoryCloudantClient.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/cloudantclient/DatabaseRepositoryCloudantClient.java @@ -21,6 +21,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.thrift.TBase; +import org.apache.thrift.TFieldIdEnum; import org.eclipse.sw360.datahandler.thrift.Source; import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; @@ -48,22 +50,15 @@ public class DatabaseRepositoryCloudantClient { private static final char HIGH_VALUE_UNICODE_CHARACTER = '\uFFF0'; private final Class type; - private final DatabaseConnectorCloudant connector; + private DatabaseConnectorCloudant connector; public void initStandardDesignDocument(Map views, - DatabaseConnectorCloudant db) { - String ddocId = "_design/" + type.getSimpleName(); + @NotNull DatabaseConnectorCloudant db) { + String ddocId = type.getSimpleName(); DesignDocument newDdoc = new DesignDocument.Builder() - .id(ddocId) + .views(views) .build(); - DesignDocument ddoc = db.get(DesignDocument.class, ddocId); - if (ddoc == null) { - db.add(newDdoc); - } - DesignDocument ddocFinal = db.get(DesignDocument.class, ddocId); - ddocFinal.setViews(views); - db.update(ddocFinal); - db.putDesignDocument(ddocFinal, ddocId); + db.putDesignDocument(newDdoc, ddocId); } public DesignDocumentViewsMapReduce createMapReduce(String map, String reduce) { @@ -104,6 +99,14 @@ public DatabaseRepositoryCloudantClient(DatabaseConnectorCloudant connector, Cla this.connector = connector; } + protected DatabaseRepositoryCloudantClient(Class type) { + this.type = type; + } + + protected void setConnector(DatabaseConnectorCloudant connector) { + this.connector = connector; + } + public Set queryForIdsAsValue(String queryName, String key) { PostViewOptions.Builder query = connector.getPostViewQueryBuilder(type, queryName); return queryForIdsAsValue(query, key); @@ -361,6 +364,12 @@ public DocumentResult updateWithResponse(T doc) { } public boolean remove(T doc) { + if (TBase.class.isAssignableFrom(doc.getClass())) { + TBase tbase = (TBase) doc; + TFieldIdEnum id = tbase.fieldForId(1); + String docId = (String) tbase.getFieldValue(id); + return connector.deleteById(docId); + } return connector.remove(doc); } diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/CommonUtils.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/CommonUtils.java index 8148a352c2..36654fff3e 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/CommonUtils.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/common/CommonUtils.java @@ -11,7 +11,6 @@ */ package org.eclipse.sw360.datahandler.common; -import com.cloudant.client.api.model.Response; import com.google.common.base.*; import com.google.common.collect.*; diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentConnector.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentConnector.java index 405dbc1ec1..176dafa317 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentConnector.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentConnector.java @@ -36,7 +36,7 @@ import org.eclipse.sw360.datahandler.thrift.attachments.CheckStatus; /** - * Ektorp connector for uploading attachments + * Connector for uploading attachments * * @author cedric.bodet@tngtech.com * @author alex.borodin@evosoft.com diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseNestedMixIn.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseNestedMixIn.java index fbccc37e7a..fe156e877b 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseNestedMixIn.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseNestedMixIn.java @@ -11,14 +11,12 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; import static org.eclipse.sw360.datahandler.common.SW360Constants.KEY_ID; import static org.eclipse.sw360.datahandler.common.SW360Constants.KEY_REV; /** - * Mixin class for Jackson serialization into CouchDB. Allows to use objects generated by Thrift directly into CouchDB - * via Ektorp. + * Mixin class for Jackson serialization into CouchDB. Allows to use objects generated by Thrift directly into CouchDB. * * @author cedric.bodet@tngtech.com */ diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java index 983527a274..5f1077dcd7 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java @@ -78,10 +78,19 @@ public NouveauLuceneAwareDatabaseConnector(@NotNull DatabaseConnectorCloudant db this.connector = dbClient; } - public boolean addDesignDoc(@NotNull NouveauDesignDocument designDocument) { + /** + * Update NouveauDesignDocument index for a database. First gets the design + * from DB if exists and update the index map to not overwrite existing + * indexes. Then puts the design to the DB. + * @param designDocument Design Document to create/add + * @return True on success. + * @throws RuntimeException If something goes wrong. + */ + public boolean addDesignDoc(@NotNull NouveauDesignDocument designDocument) + throws RuntimeException { NouveauDesignDocument documentFromDb = this.getNouveauDesignDocument(designDocument.getId()); if (documentFromDb == null) { - return putNouveauDesignDocument(designDocument, this.connector); + return putNouveauDesignDocument(designDocument); } if (!designDocument.equals(documentFromDb)) { @@ -94,7 +103,7 @@ public boolean addDesignDoc(@NotNull NouveauDesignDocument designDocument) { } }); } - return putNouveauDesignDocument(designDocument, this.connector); + return putNouveauDesignDocument(designDocument); } return true; } @@ -290,18 +299,6 @@ private static String sanitizeQueryInput(String input) { } } - private NouveauDesignDocument getNouveauDesignDocument(String id) { - if (id.isEmpty()) { - throw new IllegalArgumentException("id cannot be empty"); - } - return this.connector.get(NouveauDesignDocument.class, id); - } - - private static boolean putNouveauDesignDocument(NouveauDesignDocument designDocument, - @NotNull DatabaseConnectorCloudant connector) { - return connector.putDesignDocument(designDocument, designDocument.getId()); - } - private static @NotNull String formatDateNouveauFormat(@NotNull String date) throws ParseException { if (date.startsWith("[") && date.toUpperCase().contains(RANGE_TO)) { return formatDateRangesNouveauFormat(date); diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java index 009d3efe46..1d7af620c2 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java @@ -152,11 +152,17 @@ public static Map getIdMap(Collection in) { if (value instanceof Document doc) { return doc.getId(); } - Document doc = new Document(); Gson gson = getGson(); Type t = new TypeToken>() {}.getType(); - doc.setProperties(gson.fromJson(gson.toJson(value), t)); - return doc.getId(); + + Map map = gson.fromJson(gson.toJson(value), t); + if (map.containsKey("id")) { + return (String) map.get("id"); + } + if (map.containsKey("_id")) { + return (String) map.get("_id"); + } + return ""; }); } diff --git a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/TestUtils.java b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/TestUtils.java index abf0c89c71..f79772a3aa 100644 --- a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/TestUtils.java +++ b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/TestUtils.java @@ -9,15 +9,14 @@ */ package org.eclipse.sw360.datahandler; -import com.cloudant.client.api.CloudantClient; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.Ordering; +import com.ibm.cloud.cloudant.v1.Cloudant; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceTrackerCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; -import org.eclipse.sw360.datahandler.couchdb.DatabaseInstanceTracker; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.users.UserGroup; @@ -31,7 +30,6 @@ import java.io.IOException; import java.net.*; import java.util.*; -import java.util.function.Supplier; import static com.google.common.base.Predicates.instanceOf; import static com.google.common.base.Predicates.not; @@ -114,15 +112,13 @@ public static User getAdminUser(Class caller) { return user; } - public static void deleteDatabase(Supplier httpClient, String dbName) throws MalformedURLException { + public static void deleteDatabase(Cloudant httpClient, String dbName) throws MalformedURLException { assertTestString(dbName); DatabaseInstanceCloudant instance = new DatabaseInstanceCloudant(httpClient); if (instance.checkIfDbExists(dbName)) instance.deleteDatabase(dbName); - DatabaseInstanceTracker.destroy(); - // Giving 500ms Delay between Deleting and Creating test Db try { Thread.sleep(500); @@ -130,7 +126,7 @@ public static void deleteDatabase(Supplier httpClient, String db } } - public static void createDatabase(Supplier httpClient, String dbName) throws MalformedURLException { + public static void createDatabase(Cloudant httpClient, String dbName) throws MalformedURLException { assertTestString(dbName); DatabaseInstanceCloudant instance = new DatabaseInstanceCloudant(httpClient); diff --git a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/common/DatabaseSettingsTest.java b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/common/DatabaseSettingsTest.java index a2c19fdf7a..d35974c63f 100644 --- a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/common/DatabaseSettingsTest.java +++ b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/common/DatabaseSettingsTest.java @@ -14,6 +14,7 @@ import com.ibm.cloud.sdk.core.security.Authenticator; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; import java.net.MalformedURLException; import java.util.Properties; @@ -51,9 +52,9 @@ public class DatabaseSettingsTest { COUCH_DB_CHANGELOGS = props.getProperty("couchdb.change_logs", "sw360_test_changelogs"); } - public static Cloudant getConfiguredClient() { + public static @NotNull Cloudant getConfiguredClient() { Cloudant client; - if (!"".equals(COUCH_DB_USERNAME) && !"".equals(COUCH_DB_PASSWORD)) { + if (!COUCH_DB_USERNAME.isEmpty() && !COUCH_DB_PASSWORD.isEmpty()) { Authenticator authenticator = CouchDbSessionAuthenticator.newAuthenticator( COUCH_DB_USERNAME, COUCH_DB_PASSWORD); @@ -74,4 +75,8 @@ private DatabaseSettingsTest() { // Utility class with only static functions } + public static String getCouchDbUrl() { + return COUCH_DB_URL; + } + } diff --git a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentContentDownloaderTest.java b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentContentDownloaderTest.java index 9519468090..05acd47039 100644 --- a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentContentDownloaderTest.java +++ b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentContentDownloaderTest.java @@ -11,6 +11,7 @@ package org.eclipse.sw360.datahandler.couchdb; import com.google.common.io.CharStreams; +import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; import org.eclipse.sw360.datahandler.common.Duration; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; import org.junit.Before; @@ -21,8 +22,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.net.SocketException; -import java.net.SocketTimeoutException; import java.util.concurrent.*; import static org.eclipse.sw360.datahandler.TestUtils.*; @@ -53,7 +52,7 @@ public void setUp() throws Exception { public void testTheCouchDbUrl() throws Exception { AttachmentContent attachmentContent = mock(AttachmentContent.class); - when(attachmentContent.getRemoteUrl()).thenReturn(DatabaseTestProperties.getCouchDbUrl()); + when(attachmentContent.getRemoteUrl()).thenReturn(DatabaseSettingsTest.getCouchDbUrl()); try (InputStream download = attachmentContentDownloader.download(attachmentContent, downloadTimeout)) { String read = CharStreams.toString(new InputStreamReader(download)); diff --git a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentContentWrapperTest.java b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentContentWrapperTest.java deleted file mode 100644 index 3ba2e2b528..0000000000 --- a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentContentWrapperTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Siemens AG, 2013-2015. Part of the SW360 Portal Project. - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ - -package org.eclipse.sw360.datahandler.couchdb; - -import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; - -public class AttachmentContentWrapperTest extends DocumentWrapperTest { - - public void testUpdateNonMetadataTouchesAllFields() throws Exception { - AttachmentContent source; - source = new AttachmentContent(); - source.setFilename("a"); - source.setType("b"); - source.setContentType("v"); - source.setPartsCount("1"); - source.setRemoteUrl("uskt"); //TODO this is not required ! - - AttachmentContentWrapper attachmentContentWrapper = new AttachmentContentWrapper(); - attachmentContentWrapper.updateNonMetadata(source); - - assertTFields(source, attachmentContentWrapper, AttachmentContentWrapper.class, AttachmentContent._Fields.class); - } -} diff --git a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentStreamConnectorTest.java b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentStreamConnectorTest.java index 17493269f4..8ea810cdf6 100644 --- a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentStreamConnectorTest.java +++ b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/AttachmentStreamConnectorTest.java @@ -9,6 +9,12 @@ */ package org.eclipse.sw360.datahandler.couchdb; +import com.ibm.cloud.sdk.core.service.exception.ServiceResponseException; +import okhttp3.MediaType; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.internal.http.RealResponseBody; import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; import org.eclipse.sw360.datahandler.common.Duration; import org.eclipse.sw360.datahandler.thrift.Visibility; @@ -16,7 +22,6 @@ import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; import org.eclipse.sw360.datahandler.thrift.projects.Project; import org.eclipse.sw360.datahandler.thrift.users.User; -import org.ektorp.AttachmentInputStream; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -24,8 +29,6 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import com.cloudant.client.org.lightcouch.NoDocumentException; - import java.io.IOException; import java.io.InputStream; import java.util.Collections; @@ -108,7 +111,7 @@ public void testGetFullStream() throws Exception { String attachmentId = "id"; when(attachment.getId()).thenReturn(attachmentId); - AttachmentInputStream full = mock(AttachmentInputStream.class); + InputStream full = mock(InputStream.class); when(connector.getAttachment(attachmentId, "fil")).thenReturn(full); when(full.read()).thenReturn(1, 2, -1); @@ -136,10 +139,10 @@ public void testGetConcatenatedStream() throws Exception { String attachmentId = "id"; when(attachment.getId()).thenReturn(attachmentId); - AttachmentInputStream part1 = mock(AttachmentInputStream.class); + InputStream part1 = mock(InputStream.class); when(connector.getAttachment(attachmentId, "fil_part1")).thenReturn(part1); - AttachmentInputStream part2 = mock(AttachmentInputStream.class); + InputStream part2 = mock(InputStream.class); when(connector.getAttachment(attachmentId, "fil_part2")).thenReturn(part2); when(part1.read()).thenReturn(1, -1); @@ -171,10 +174,18 @@ public void testGetConcatenatedStreamReadThrowsOnNonExistent() throws Exception String attachmentId = "id"; when(attachment.getId()).thenReturn(attachmentId); - AttachmentInputStream part1 = mock(AttachmentInputStream.class); + InputStream part1 = mock(InputStream.class); when(connector.getAttachment(attachmentId, "fil_part1")).thenReturn(part1); - when(connector.getAttachment(attachmentId, "fil_part2")).thenThrow(new NoDocumentException("")); + when(connector.getAttachment(attachmentId, "fil_part2")).thenThrow( + new ServiceResponseException(404, new Response.Builder() + .code(404) + .request(new Request.Builder().url("http://example.com").build()) + .protocol(Protocol.HTTP_1_0) + .message("Not Found") + .body(RealResponseBody.create("Not Found", MediaType.get("text/plain"))) + .build()) + ); when(part1.read()).thenReturn(1, -1); InputStream attachmentStream = attachmentStreamConnector.getAttachmentStream(attachment, dummyUser, diff --git a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/DatabaseConnectorTest.java b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/DatabaseConnectorTest.java index bbafa55288..c1d252f27a 100644 --- a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/DatabaseConnectorTest.java +++ b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/DatabaseConnectorTest.java @@ -10,21 +10,20 @@ package org.eclipse.sw360.datahandler.couchdb; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; +import com.ibm.cloud.cloudant.v1.Cloudant; +import com.ibm.cloud.cloudant.v1.model.DocumentResult; +import com.ibm.cloud.cloudant.v1.model.PostDocumentOptions; +import com.ibm.cloud.cloudant.v1.model.PutDatabaseOptions; +import com.ibm.cloud.sdk.core.service.exception.ServiceResponseException; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; +import org.eclipse.sw360.datahandler.common.DatabaseSettingsTest; import org.eclipse.sw360.testthrift.TestObject; -import org.ektorp.CouchDbConnector; -import org.ektorp.CouchDbInstance; -import org.ektorp.http.HttpClient; -import org.ektorp.impl.StdCouchDbConnector; -import org.ektorp.impl.StdCouchDbInstance; import org.junit.After; import org.junit.Before; import org.junit.Test; -import java.util.Collections; - -import static org.eclipse.sw360.datahandler.couchdb.DatabaseTestProperties.COUCH_DB_DATABASE; +import static org.eclipse.sw360.datahandler.common.DatabaseSettingsTest.COUCH_DB_DATABASE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -32,8 +31,7 @@ public class DatabaseConnectorTest { - DatabaseConnector connector; - MapperFactory factory; + DatabaseConnectorCloudant connector; TestObject object; @@ -47,44 +45,50 @@ public void setUp() throws Exception { object.setName("Test"); object.setText("This is some nice test text."); // Initialize the mapper factory - factory = new MapperFactory(ImmutableList.>of(TestObject.class), Collections.>emptyList(), Maps.newHashMap()); - // Default connector for testing - HttpClient httpClient = DatabaseTestProperties.getConfiguredHttpClient(); - CouchDbInstance dbInstance = new StdCouchDbInstance(httpClient); + Cloudant client = DatabaseSettingsTest.getConfiguredClient(); + DatabaseInstanceCloudant dbInstance = new DatabaseInstanceCloudant(client); // Create database if it does not exists if (!dbInstance.checkIfDbExists(COUCH_DB_DATABASE)) { - dbInstance.createDatabase(COUCH_DB_DATABASE); + PutDatabaseOptions putDbOptions = new PutDatabaseOptions.Builder().db(COUCH_DB_DATABASE).build(); + try { + client.putDatabase(putDbOptions).execute().getResult(); + } catch (ServiceResponseException e) { + if (e.getStatusCode() != 412) { + throw e; + } + } } - CouchDbConnector db = new StdCouchDbConnector(COUCH_DB_DATABASE, dbInstance, factory); + // Now create the actual database connector + connector = new DatabaseConnectorCloudant(client, COUCH_DB_DATABASE); + // Add the object - db.create(object); + PostDocumentOptions postDocOption = new PostDocumentOptions.Builder() + .db(COUCH_DB_DATABASE) + .document(connector.getDocumentFromPojo(object)) + .build(); + + DocumentResult resp = client.postDocument(postDocOption).execute().getResult(); // Save id and rev for teardown - id = object.getId(); - rev = object.getRevision(); - // Now create the actual database connector - connector = new DatabaseConnector(DatabaseTestProperties.getConfiguredHttpClient(), COUCH_DB_DATABASE, factory); + id = resp.getId(); + rev = resp.getRev(); + object.setId(id); + object.setRevision(rev); } @After public void tearDown() throws Exception { // Default connector for testing - HttpClient httpClient = DatabaseTestProperties.getConfiguredHttpClient(); - CouchDbInstance dbInstance = new StdCouchDbInstance(httpClient); - if (dbInstance.checkIfDbExists(COUCH_DB_DATABASE)) { - dbInstance.deleteDatabase(COUCH_DB_DATABASE); + if (connector.getInstance().checkIfDbExists(COUCH_DB_DATABASE)) { + connector.getInstance().deleteDatabase(COUCH_DB_DATABASE); } } @Test public void testSetUp() throws Exception { - // Default connector for testing - HttpClient httpClient = DatabaseTestProperties.getConfiguredHttpClient(); - CouchDbInstance dbInstance = new StdCouchDbInstance(httpClient); - CouchDbConnector db = new StdCouchDbConnector(COUCH_DB_DATABASE, dbInstance, factory); // Check that the document was inserted - assertTrue(db.contains(id)); + assertTrue(connector.contains(id)); } @@ -119,7 +123,7 @@ public void testUpdateDocument() throws Exception { object.setText("Some new text"); // Update the document connector.update(object); - // Checkt that the object's revision was updated + // Check that the object's revision was updated assertNotEquals(rev, object.getRevision()); // Fetch it again to check it was updated in the database TestObject object1 = connector.get(TestObject.class, id); diff --git a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/DatabaseTestProperties.java b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/DatabaseTestProperties.java deleted file mode 100644 index 06c71990cd..0000000000 --- a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/DatabaseTestProperties.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright Siemens AG, 2013-2015, 2019. Part of the SW360 Portal Project. - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ - -package org.eclipse.sw360.datahandler.couchdb; - -import org.eclipse.sw360.datahandler.common.CommonUtils; -import org.ektorp.http.HttpClient; -import org.ektorp.http.StdHttpClient; - -import java.net.MalformedURLException; -import java.util.Optional; -import java.util.Properties; - -/** - * Properties class for database connection tests. - * - * @author cedric.bodet@tngtech.com - */ -public class DatabaseTestProperties { - - private static final String PROPERTIES_FILE_PATH = "/couchdb-test.properties"; - - private static final String COUCH_DB_URL; - public static final String COUCH_DB_DATABASE; - public static final boolean COUCH_DB_CACHE; - - private static final Optional COUCH_DB_USERNAME; - private static final Optional COUCH_DB_PASSWORD; - - static { - Properties props = CommonUtils.loadProperties(DatabaseTestProperties.class, PROPERTIES_FILE_PATH); - - COUCH_DB_URL = props.getProperty("couchdb.url", "http://localhost:5984"); - COUCH_DB_DATABASE = props.getProperty("couchdb.database", "sw360_test_db"); - COUCH_DB_USERNAME = Optional.ofNullable(props.getProperty("couchdb.user", "")); - COUCH_DB_PASSWORD = Optional.ofNullable(props.getProperty("couchdb.password", "")); - COUCH_DB_CACHE = Boolean.parseBoolean(props.getProperty("couchdb.cache", "true")); - } - - public static HttpClient getConfiguredHttpClient() throws MalformedURLException { - StdHttpClient.Builder httpClientBuilder = new StdHttpClient.Builder().url(COUCH_DB_URL); - if (!COUCH_DB_CACHE) { - httpClientBuilder.caching(false); - } - if (COUCH_DB_USERNAME.isPresent() && COUCH_DB_PASSWORD.isPresent()) { - httpClientBuilder.username(COUCH_DB_USERNAME.get()); - httpClientBuilder.password(COUCH_DB_PASSWORD.get()); - } - return httpClientBuilder.build(); - } - - public static String getCouchDbUrl() { - return COUCH_DB_URL; - } -} diff --git a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/DocumentWrapperTest.java b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/DocumentWrapperTest.java deleted file mode 100644 index a76260e54f..0000000000 --- a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/DocumentWrapperTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright Siemens AG, 2013-2015. Part of the SW360 Portal Project. - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ - -package org.eclipse.sw360.datahandler.couchdb; - -import com.google.common.collect.ImmutableList; -import junit.framework.TestCase; -import org.apache.thrift.TBase; -import org.apache.thrift.TFieldIdEnum; - -import java.lang.reflect.Field; - -// test that the wrapper class W correctly wraps class C, which has fields C (F is always C._Fields) -public abstract class DocumentWrapperTest, C extends TBase, F extends Enum & TFieldIdEnum> extends TestCase { - private static final ImmutableList NOT_COPIED_FIELDS = ImmutableList.of("id", "revision"); - - protected void assertTFields(C source, W attachmentWrapper, Class wrapperClass, Class fieldClass) throws IllegalAccessException { - for (F thriftField : fieldClass.getEnumConstants()) { - final String sourceFieldName = thriftField.getFieldName(); - final Object sourceFieldValue = source.getFieldValue(thriftField); - - if (!NOT_COPIED_FIELDS.contains(sourceFieldName)) - assertNotNull("please set the field " + sourceFieldName + " in this test", sourceFieldValue); - - final Field field = getField(wrapperClass, sourceFieldName); - assertNotNull("field " + sourceFieldName + " is not defined in " + wrapperClass.getName(), field); - - field.setAccessible(true); - final Object copyFiledValue = field.get(attachmentWrapper); - - if (field.getType().isPrimitive()) { - assertEquals(copyFiledValue, sourceFieldValue); - } else { - assertSame(copyFiledValue, sourceFieldValue); - } - } - } - - private Field getField(Class wrapperClass, String sourceFieldName) { - return getField(wrapperClass, sourceFieldName, 0); - } - - private Field getField(Class attachmentWrapperClass, String sourceFieldName, int depth) { - if (attachmentWrapperClass == null) { - return null; - } - try { - return attachmentWrapperClass.getDeclaredField(sourceFieldName); - } catch (NoSuchFieldException e) { - final Class superclass = attachmentWrapperClass.getSuperclass(); - return getField(superclass, sourceFieldName, depth + 1); - } - } - -} \ No newline at end of file diff --git a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/MapperFactoryTest.java b/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/MapperFactoryTest.java deleted file mode 100644 index 49dbf087d3..0000000000 --- a/libraries/datahandler/src/test/java/org/eclipse/sw360/datahandler/couchdb/MapperFactoryTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright Siemens AG, 2013-2017. Part of the SW360 Portal Project. - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ - -package org.eclipse.sw360.datahandler.couchdb; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; -import org.eclipse.sw360.testthrift.TestObject; -import org.junit.Before; -import org.junit.Test; - -import java.util.Collections; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -/** - * @author cedric.bodet@tngtech.com - */ -public class MapperFactoryTest { - - private static final String TEST_ID = "abcdef"; - private static final String TEST_REV = "123456"; - private static final String TEST_NAME = "Super License 3.2"; - - ObjectMapper mapper; - TestObject object; - - @Before - public void setUp() throws Exception { - // Prepare mapper - MapperFactory factory = new MapperFactory(ImmutableList.>of(TestObject.class), Collections.>emptyList(), - Maps.newHashMap()); - mapper = factory.createObjectMapper(); - // Prepare object - object = new TestObject(); - object.setId(TEST_ID); - object.setRevision(TEST_REV); - object.setName(TEST_NAME); - } - - @Test - public void testLicenseSerialization() { - // Serialize the object (as node) - JsonNode node = mapper.valueToTree(object); - // Check that fields are present - assertTrue("_id present", node.has("_id")); - assertTrue("_rev present", node.has("_rev")); - assertTrue("name present", node.has("name")); - // Text was not set - assertFalse("Text not set", node.has("text")); - } - - @Test - public void testLicenseContent() { - // Serialize the object (as node) - JsonNode node = mapper.valueToTree(object); - // Check field values - assertEquals(TEST_ID, node.get("_id").textValue()); - assertEquals(TEST_REV, node.get("_rev").textValue()); - assertEquals(TEST_NAME, node.get("name").textValue()); - } - - @Test - public void testLicenseDeserialization() throws Exception { - // Serialize the object (as string) - String string = mapper.writeValueAsString(object); - // Deserialize the object - TestObject parsedObject = mapper.readValue(string, TestObject.class); - - // Check field values - assertEquals(TEST_ID, parsedObject.getId()); - assertEquals(TEST_REV, parsedObject.getRevision()); - assertEquals(TEST_NAME, parsedObject.getName()); - assertNull("test not present", parsedObject.getText()); - } - - @Test - public void testNullValues() throws Exception { - // Null _id and _rev should not be serialized, as they are not accepted by CouchDB - object.unsetId(); - object.unsetRevision(); - // Serialize the object (as node) - JsonNode node = mapper.valueToTree(object); - // Check that null-fields are not-present - assertFalse("_id present", node.has("_id")); - assertFalse("_rev present", node.has("_rev")); - // Name should still be present - assertTrue("name present", node.has("name")); - } -} diff --git a/libraries/importers/src/test/java/org/eclipse/sw360/importer/ComponentAndAttachmentAwareDBTest.java b/libraries/importers/src/test/java/org/eclipse/sw360/importer/ComponentAndAttachmentAwareDBTest.java index 4f6a235a77..4d1db0755f 100644 --- a/libraries/importers/src/test/java/org/eclipse/sw360/importer/ComponentAndAttachmentAwareDBTest.java +++ b/libraries/importers/src/test/java/org/eclipse/sw360/importer/ComponentAndAttachmentAwareDBTest.java @@ -82,7 +82,7 @@ protected static ThriftClients getThriftClients() throws TException, IOException ThriftClients thriftClients = failingMock(ThriftClients.class); ComponentHandler componentHandler = new ComponentHandler(DatabaseSettingsTest.getConfiguredClient(),DatabaseSettingsTest.COUCH_DB_DATABASE, DatabaseSettingsTest.COUCH_DB_CHANGELOGS, DatabaseSettingsTest.COUCH_DB_ATTACHMENTS, thriftClients); - VendorHandler vendorHandler = new VendorHandler(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.getConfiguredHttpClient(), DatabaseSettingsTest.COUCH_DB_DATABASE); + VendorHandler vendorHandler = new VendorHandler(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.COUCH_DB_DATABASE); AttachmentHandler attachmentHandler = new AttachmentHandler(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.COUCH_DB_DATABASE, DatabaseSettingsTest.COUCH_DB_ATTACHMENTS); ModerationService.Iface moderationService = failingMock(ModerationService.Iface.class); diff --git a/libraries/nouveau-handler/pom.xml b/libraries/nouveau-handler/pom.xml index 7ac18044ec..7061051b4e 100644 --- a/libraries/nouveau-handler/pom.xml +++ b/libraries/nouveau-handler/pom.xml @@ -164,11 +164,6 @@ - - com.cloudant - cloudant-client - ${cloudant.version} - com.ibm.cloud cloudant diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java index 76030d1988..8bdea73f97 100644 --- a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java @@ -13,11 +13,15 @@ import com.google.gson.reflect.TypeToken; import com.ibm.cloud.cloudant.common.SdkCommon; import com.ibm.cloud.cloudant.v1.Cloudant; +import com.ibm.cloud.cloudant.v1.model.DocumentResult; +import com.ibm.cloud.cloudant.v1.model.PutDesignDocumentOptions; import com.ibm.cloud.sdk.core.http.RequestBuilder; import com.ibm.cloud.sdk.core.http.ResponseConverter; import com.ibm.cloud.sdk.core.http.ServiceCall; +import com.ibm.cloud.sdk.core.service.exception.NotFoundException; import com.ibm.cloud.sdk.core.util.ResponseConverterUtils; import com.ibm.cloud.sdk.core.util.Validator; +import org.eclipse.sw360.nouveau.designdocument.NouveauDesignDocument; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -29,7 +33,7 @@ */ public class LuceneAwareCouchDbConnector { public static String DEFAULT_NOUVEAU_PREFIX = "_nouveau"; - public static String DEFAULT_DESIGN_PREFIX = "_design"; + public static String DEFAULT_DESIGN_PREFIX = ""; // '_design/' not needed with Cloudant SDK private final NouveauAwareDatabase database; @@ -49,6 +53,7 @@ public static class NouveauAwareDatabase extends Cloudant { public NouveauAwareDatabase(@NotNull Cloudant client, String db, String ddoc, String lucenePrefix, Gson gson) { super(client.getName(), client.getAuthenticator()); + this.setServiceUrl(client.getServiceUrl()); this.db = db; this.ddoc = ddoc; this.lucenePrefix = lucenePrefix; @@ -68,7 +73,7 @@ public ServiceCall queryNouveau(String index, @NotNull NouveauQuery query RequestBuilder builder = RequestBuilder.post(RequestBuilder.resolveRequestUrl( this.getServiceUrl(), - "/{db}/{ddoc}/{nouveauPrefix}/{index}", + "/{db}/_design/{ddoc}/{nouveauPrefix}/{index}", pathParamsMap)); Map sdkHeaders = SdkCommon.getSdkHeaders("cloudant", "v1", "postNouveauQuery"); @@ -84,6 +89,78 @@ public ServiceCall queryNouveau(String index, @NotNull NouveauQuery query }).getType()); return this.createServiceCall(builder.build(), responseConverter); } + + public ServiceCall getNouveauDesignDocument(String ddoc) { + Validator.notEmpty(ddoc, "ddoc cannot be empty"); + + Map pathParamsMap = new HashMap<>(); + pathParamsMap.put("db", this.db); + pathParamsMap.put("ddoc", ensureDesignId(this.ddoc)); + + RequestBuilder builder = RequestBuilder.get(RequestBuilder.resolveRequestUrl( + this.getServiceUrl(), + "/{db}/_design/{ddoc}", + pathParamsMap)); + Map sdkHeaders = SdkCommon.getSdkHeaders("cloudant", "v1", "getNouveauDesignDocument"); + + for (Map.Entry stringStringEntry : sdkHeaders.entrySet()) { + builder.header(stringStringEntry.getKey(), stringStringEntry.getValue()); + } + + builder.header("Accept", "application/json"); + builder.header("Content-Type", "application/json"); + + builder.query("latest", String.valueOf(true)); + + ResponseConverter responseConverter = ResponseConverterUtils.getValue((new TypeToken() { + }).getType()); + return this.createServiceCall(builder.build(), responseConverter); + } + + /** + * Put a NouveauDesignDocument to DB. + * First checks if the document already exists to get the ID and + * revision update. If not, then creates a new one. + * @param designDocument Design document to create/update + * @return true on success + * @throws RuntimeException If there is some error. + */ + public boolean putNouveauDesignDocument( + @NotNull NouveauDesignDocument designDocument + ) throws RuntimeException { + NouveauDesignDocument existingDoc; + try { + existingDoc = this.getNouveauDesignDocument(designDocument.getId()) + .execute() + .getResult(); + } catch (NotFoundException e) { + existingDoc = null; + } + if (existingDoc != null) { + designDocument.setId(existingDoc.getId()); + designDocument.setRev(existingDoc.getRev()); + } + PutDesignDocumentOptions designDocumentOptions = + new PutDesignDocumentOptions.Builder() + .db(this.db) + .designDocument(designDocument) + .ddoc(ddoc) + .build(); + + DocumentResult response = + this.putDesignDocument(designDocumentOptions) + .execute().getResult(); + boolean success = response.isOk(); + if (!success) { + throw new RuntimeException( + "Unable to put design document " + designDocument.getId() + + " to " + ddoc + ". Error: " + response.getError()); + } else { + designDocument.setId(response.getId()); + designDocument.setRev(response.getRev()); + } + return true; + } } /** @@ -103,10 +180,40 @@ public NouveauResult queryNouveau(String index, @NotNull NouveauQuery query) { */ @Contract(pure = true) public static @NotNull String ensureDesignId(@NotNull String designId) { - if (designId.startsWith(DEFAULT_DESIGN_PREFIX)) { - return designId; + if (!DEFAULT_DESIGN_PREFIX.isEmpty() && !designId.startsWith(DEFAULT_DESIGN_PREFIX)) { + return DEFAULT_DESIGN_PREFIX + designId; } else { - return DEFAULT_DESIGN_PREFIX + "/" + designId; + return designId; } } + + /** + * Get a NouveauDesignDocument from DB. + * @param ddoc ddoc to get + * @return Design document if found. Null otherwise. + */ + protected NouveauDesignDocument getNouveauDesignDocument(@NotNull String ddoc) { + if (ddoc.isEmpty()) { + throw new IllegalArgumentException("ddoc cannot be empty"); + } + try { + return this.database.getNouveauDesignDocument(ddoc) + .execute() + .getResult(); + } catch (NotFoundException e) { + return null; + } + } + + /** + * Create/update Nouveau design document. + * @param designDocument Design document to create/update. + * @return True on success. + * @throws RuntimeException If something goes wrong. + */ + public boolean putNouveauDesignDocument( + @NotNull NouveauDesignDocument designDocument + ) throws RuntimeException { + return this.database.putNouveauDesignDocument(designDocument); + } } diff --git a/libraries/nouveau-handler/src/test/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnectorTest.java b/libraries/nouveau-handler/src/test/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnectorTest.java index 2927d824cd..e9c5776068 100644 --- a/libraries/nouveau-handler/src/test/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnectorTest.java +++ b/libraries/nouveau-handler/src/test/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnectorTest.java @@ -15,10 +15,10 @@ public class LuceneAwareCouchDbConnectorTest extends TestCase { public void testEnsureDesignIdMissing() { - assert "_design/lucene".equals(LuceneAwareCouchDbConnector.ensureDesignId("lucene")); + assert (LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX + "lucene").equals(LuceneAwareCouchDbConnector.ensureDesignId("lucene")); } public void testEnsureDesignIdContaining() { - assert "_design/lucene".equals(LuceneAwareCouchDbConnector.ensureDesignId("_design/lucene")); + assert (LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX + "lucene").equals(LuceneAwareCouchDbConnector.ensureDesignId(LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX + "lucene")); } } diff --git a/pom.xml b/pom.xml index 0a978126bd..b248d273c2 100644 --- a/pom.xml +++ b/pom.xml @@ -90,7 +90,6 @@ 2.14.2 1.12.13 - 2.19.1 0.9.1 1.15 4.4 @@ -120,7 +119,6 @@ 4.13.2 3.6.2 2.19.0 - 6.6.6 4.7.0 4.10.0 1.1.1 @@ -391,11 +389,6 @@ 3.11.1 test - - com.cloudant - cloudant-client - ${cloudant.version} - com.ibm.cloud cloudant diff --git a/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/persistence/OAuthClientEntity.java b/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/persistence/OAuthClientEntity.java index b12e8d640f..cdc3ec8af6 100644 --- a/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/persistence/OAuthClientEntity.java +++ b/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/persistence/OAuthClientEntity.java @@ -9,19 +9,23 @@ */ package org.eclipse.sw360.rest.authserver.client.persistence; +import java.io.Serial; +import java.io.Serializable; import java.util.Collection; import java.util.Set; -import org.ektorp.support.CouchDbDocument; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -public class OAuthClientEntity extends CouchDbDocument { +public class OAuthClientEntity implements Serializable { + @Serial private static final long serialVersionUID = 1L; + private String id; + private String revision; private String clientId; private String clientSecret; private String description; @@ -36,6 +40,26 @@ public class OAuthClientEntity extends CouchDbDocument { private Integer refreshTokenValiditySeconds; private Set autoApproveScopes; + @JsonProperty("_id") + public void setId(String id) { + this.id = id; + } + + @JsonProperty("_id") + public String getId() { + return id; + } + + @JsonProperty("_rev") + public void setRevision(String revision) { + this.revision = revision; + } + + @JsonProperty("_rev") + public String getRevision() { + return revision; + } + @JsonProperty("secretRequired") public void setSecretRequired(boolean secretRequired) { this.secretRequired = secretRequired; diff --git a/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/persistence/OAuthClientRepository.java b/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/persistence/OAuthClientRepository.java index dba5aa7202..8cbee176de 100644 --- a/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/persistence/OAuthClientRepository.java +++ b/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/persistence/OAuthClientRepository.java @@ -9,21 +9,19 @@ */ package org.eclipse.sw360.rest.authserver.client.persistence; -import org.ektorp.ViewQuery; -import org.ektorp.ViewResult; -import org.ektorp.http.StdHttpClient; -import org.ektorp.impl.StdCouchDbConnector; -import org.ektorp.impl.StdCouchDbInstance; -import org.ektorp.support.CouchDbRepositorySupport; -import org.ektorp.support.View; -import org.ektorp.support.Views; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; +import org.eclipse.sw360.datahandler.common.DatabaseSettings; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import com.google.common.collect.Lists; -import java.net.MalformedURLException; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; /** * This repository can perform CRUD operations on a CouchDB for @@ -31,47 +29,49 @@ * connection has to be available to Spring's {@link Value} infrastructure. */ @Component -@Views({ - @View(name = "all", map = "function(doc) { emit(null, doc._id); }"), - @View(name = "byId", map = "function(doc) { emit(doc._id, null); }"), - @View(name = "byClientId", map = "function(doc) { emit(doc.client_id, null); }") -}) -public class OAuthClientRepository extends CouchDbRepositorySupport { +public class OAuthClientRepository extends DatabaseRepositoryCloudantClient { - protected OAuthClientRepository( - @Value("${couchdb.url}") final String dbUrl, - @Value("${couchdb.database}") final String dbName, - @Value("${couchdb.username:#{null}}") final String dbUsername, - @Value("${couchdb.password:#{null}}") final String dbPassword) throws MalformedURLException { + private static final String ALL = + "function(doc) {" + + " emit(null, doc._id); " + + "}"; - super(OAuthClientEntity.class, new StdCouchDbConnector(dbName, - new StdCouchDbInstance( - new StdHttpClient.Builder() - .caching(false) - .url(dbUrl) - .username(dbUsername) - .password(dbPassword) - .build() - ) - ) - ); + private static final String BY_IDs_VIEW = + "function(doc) {" + + " emit(doc._id, null); " + + "}"; - initStandardDesignDocument(); + private static final String BY_CLIENT_ID_VIEW = + "function(doc) {" + + " emit(doc.client_id, null); " + + "}"; + + public OAuthClientRepository() { + super(OAuthClientEntity.class); + + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(DatabaseSettings.getConfiguredClient(), + DatabaseSettings.COUCH_DB_DATABASE); + + super.setConnector(db); + + Map views = new HashMap<>(); + views.put("all", createMapReduce(ALL, null)); + views.put("byids", createMapReduce(BY_IDs_VIEW, null)); + views.put("byClientId", createMapReduce(BY_CLIENT_ID_VIEW, null)); + initStandardDesignDocument(views, db); } public OAuthClientEntity getByClientId(String clientId) { - ViewQuery query = createQuery("byClientId"); - query.setIgnoreNotFound(true); - query.key(clientId); + final Set idList = queryForIds("byClientId", clientId); List clients = Lists.newArrayList(); - ViewResult result = db.queryView(query); - for (ViewResult.Row row : result.getRows()) { - String id = row.getId(); - clients.add(super.get(id)); + if (idList != null) { + for (String id : idList) { + clients.add(super.get(id)); + } } - if (clients.size() < 1) { + if (clients.isEmpty()) { log.warn("No clients found for clientId <{}>.", clientId); return null; } else if (clients.size() > 1) { diff --git a/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/rest/OAuthClientController.java b/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/rest/OAuthClientController.java index 8f6c678464..1646c80ac5 100644 --- a/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/rest/OAuthClientController.java +++ b/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/rest/OAuthClientController.java @@ -49,7 +49,7 @@ public class OAuthClientController { public static final String ENDPOINT_URL = "client-management"; - private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); @Value("${security.oauth2.resource.id}") private String resourceId; diff --git a/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/rest/OAuthClientResource.java b/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/rest/OAuthClientResource.java index f1d22d1134..3323f19c4d 100644 --- a/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/rest/OAuthClientResource.java +++ b/rest/authorization-server/src/main/java/org/eclipse/sw360/rest/authserver/client/rest/OAuthClientResource.java @@ -11,12 +11,16 @@ import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.Setter; import org.eclipse.sw360.rest.authserver.client.persistence.OAuthClientEntity; import java.util.Set; +@Getter public class OAuthClientResource { + @Setter @JsonProperty("description") private String description; @@ -51,37 +55,4 @@ public OAuthClientResource(OAuthClientEntity clientEntity) { this.accessTokenValidity = clientEntity.getAccessTokenValiditySeconds(); this.refreshTokenValidity = clientEntity.getRefreshTokenValiditySeconds(); } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getClientId() { - return clientId; - } - - public String getClientSecret() { - return clientSecret; - } - - public Set getAuthorities() { - return authorities; - } - - public Set getScope() { - return scope; - } - - public Integer getAccessTokenValidity() { - return accessTokenValidity; - } - - public Integer getRefreshTokenValidity() { - return refreshTokenValidity; - } - } diff --git a/rest/authorization-server/src/test/java/org/eclipse/sw360/rest/authserver/client/persistence/OAuthClientRepositoryTest.java b/rest/authorization-server/src/test/java/org/eclipse/sw360/rest/authserver/client/persistence/OAuthClientRepositoryTest.java index 33974a3bc8..303d9a7cd6 100644 --- a/rest/authorization-server/src/test/java/org/eclipse/sw360/rest/authserver/client/persistence/OAuthClientRepositoryTest.java +++ b/rest/authorization-server/src/test/java/org/eclipse/sw360/rest/authserver/client/persistence/OAuthClientRepositoryTest.java @@ -27,7 +27,7 @@ * loading and overwriting in tests. * * Currently we are only testing the setup anyway, since we do not have custom - * repo logic yet - beside the ektorp supported CRUD logic. + * repo logic yet. * * ATTENTION: This test should be executed manually when a couchdb is running. * Make sure to not have the OAuthClientRepository as Mockito mock in the diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/SW360RestHealthIndicator.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/SW360RestHealthIndicator.java index 5729bba92a..5ef543d554 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/SW360RestHealthIndicator.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/SW360RestHealthIndicator.java @@ -12,8 +12,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; import org.apache.thrift.TException; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettings; -import org.eclipse.sw360.datahandler.couchdb.DatabaseInstance; import org.eclipse.sw360.datahandler.thrift.ThriftClients; import org.eclipse.sw360.datahandler.thrift.health.HealthService; import org.eclipse.sw360.datahandler.thrift.health.Status; @@ -21,7 +21,6 @@ import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; -import java.net.MalformedURLException; import java.util.ArrayList; import java.util.List; @@ -48,18 +47,13 @@ public Health health() { private RestState check(List exception) { RestState restState = new RestState(); - try { - restState.isDbReachable = isDbReachable(exception); - } catch (MalformedURLException e) { - restState.isDbReachable = false; - exception.add(e); - } + restState.isDbReachable = isDbReachable(exception); restState.isThriftReachable = isThriftReachable(exception); return restState; } - private boolean isDbReachable(List exception) throws MalformedURLException { - DatabaseInstance databaseInstance = makeDatabaseInstance(); + private boolean isDbReachable(List exception) { + DatabaseInstanceCloudant databaseInstance = makeDatabaseInstance(); try { return databaseInstance.checkIfDbExists(DatabaseSettings.COUCH_DB_ATTACHMENTS); } catch (Exception e) { @@ -90,8 +84,8 @@ protected HealthService.Iface makeHealthClient() { return new ThriftClients().makeHealthClient(); } - protected DatabaseInstance makeDatabaseInstance() throws MalformedURLException { - return new DatabaseInstance(DatabaseSettings.getConfiguredHttpClient().get()); + protected DatabaseInstanceCloudant makeDatabaseInstance() { + return new DatabaseInstanceCloudant(DatabaseSettings.getConfiguredClient()); } public static class RestState { diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/core/RestControllerHelper.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/core/RestControllerHelper.java index 982ab30548..9ee95b9e1e 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/core/RestControllerHelper.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/core/RestControllerHelper.java @@ -1169,12 +1169,12 @@ public boolean setDataForVulnerability(VulnerabilityApiDTO vulnerabilityApiDTO, if (fieldDTO.equals(VulnerabilityApiDTO._Fields.CVSS)) { if (!setDataCVSS(vulnerabilityApiDTO.getCvss(), vulnerability)) { throw new RuntimeException(new SW360Exception("Invalid cvss: property 'cvss' should be a valid cvss.") - .setErrorCode(org.apache.http.HttpStatus.SC_BAD_REQUEST)); + .setErrorCode(org.apache.hc.core5.http.HttpStatus.SC_BAD_REQUEST)); } } else if (fieldDTO.equals(VulnerabilityApiDTO._Fields.IS_SET_CVSS)) { if(!setDataIsSetCvss(vulnerabilityApiDTO.getIsSetCvss(), vulnerability)) { throw new RuntimeException(new SW360Exception("Invalid isSetCvss: property 'isSetCvss' should be a valid isSetCvss.") - .setErrorCode(org.apache.http.HttpStatus.SC_BAD_REQUEST)); + .setErrorCode(org.apache.hc.core5.http.HttpStatus.SC_BAD_REQUEST)); } } else if (fieldDTO.equals(VulnerabilityApiDTO._Fields.CVE_REFERENCES)) { setDataCveReferences(vulnerabilityApiDTO.getCveReferences(), vulnerability); @@ -1223,16 +1223,16 @@ private void setDataCveReferences(Set cveReferences, Vulnerability vulne for (String cveReference : cveReferences) { if (CommonUtils.isNullEmptyOrWhitespace(cveReference)) { throw new RuntimeException(new SW360Exception("Invalid yearNumber: property 'yearNumber' cannot be null, empty or whitespace.") - .setErrorCode(org.apache.http.HttpStatus.SC_BAD_REQUEST)); + .setErrorCode(org.apache.hc.core5.http.HttpStatus.SC_BAD_REQUEST)); } if (!Pattern.matches("^\\d{4}-\\d*", cveReference)) { throw new RuntimeException(new SW360Exception("Invalid yearNumber: property 'yearNumber' is wrong format") - .setErrorCode(org.apache.http.HttpStatus.SC_BAD_REQUEST)); + .setErrorCode(org.apache.hc.core5.http.HttpStatus.SC_BAD_REQUEST)); } String[] yearAndNumber = cveReference.split("-"); if (yearAndNumber.length != 2) { throw new RuntimeException(new SW360Exception("Invalid yearNumber: property 'year-Number' is wrong format") - .setErrorCode(org.apache.http.HttpStatus.SC_BAD_REQUEST)); + .setErrorCode(org.apache.hc.core5.http.HttpStatus.SC_BAD_REQUEST)); } CVEReference m_cVEReference = new CVEReference(); m_cVEReference.setYear(yearAndNumber[0]); diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/vulnerability/Sw360VulnerabilityService.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/vulnerability/Sw360VulnerabilityService.java index af58b11039..35b7998642 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/vulnerability/Sw360VulnerabilityService.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/vulnerability/Sw360VulnerabilityService.java @@ -12,7 +12,7 @@ import java.util.*; -import org.apache.http.HttpStatus; +import org.apache.hc.core5.http.HttpStatus; import org.apache.thrift.TException; import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.protocol.TProtocol; diff --git a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/SW360RestHealthIndicatorTest.java b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/SW360RestHealthIndicatorTest.java index dd47cb4d3a..1d755df974 100644 --- a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/SW360RestHealthIndicatorTest.java +++ b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/SW360RestHealthIndicatorTest.java @@ -11,8 +11,8 @@ import org.apache.thrift.TException; import org.apache.thrift.transport.TTransportException; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseInstanceCloudant; import org.eclipse.sw360.datahandler.common.DatabaseSettings; -import org.eclipse.sw360.datahandler.couchdb.DatabaseInstance; import org.eclipse.sw360.datahandler.thrift.health.Health; import org.eclipse.sw360.datahandler.thrift.health.HealthService; import org.eclipse.sw360.datahandler.thrift.health.Status; @@ -61,7 +61,7 @@ public class SW360RestHealthIndicatorTest { private static final String IS_THRIFT_REACHABLE = "isThriftReachable"; private static final String ERROR = "error"; - private DatabaseInstance databaseInstanceMock; + private DatabaseInstanceCloudant databaseInstanceMock; /** * Makes a request to localhost with the default server port and returns @@ -83,7 +83,7 @@ public void info_should_return_200() { @Test public void health_should_return_503_with_missing_db() throws TException, MalformedURLException { - databaseInstanceMock = mock(DatabaseInstance.class); + databaseInstanceMock = mock(DatabaseInstanceCloudant.class); when(databaseInstanceMock.checkIfDbExists(anyString())) .thenReturn(false); @@ -113,7 +113,7 @@ public void health_should_return_503_with_missing_db() throws TException, Malfor @Test public void health_should_return_503_with_unhealthy_thrift() throws TException, MalformedURLException { - databaseInstanceMock = mock(DatabaseInstance.class); + databaseInstanceMock = mock(DatabaseInstanceCloudant.class); when(databaseInstanceMock.checkIfDbExists(anyString())) .thenReturn(true); @@ -148,7 +148,7 @@ public void health_should_return_503_with_unhealthy_thrift() throws TException, @Test public void health_should_return_503_with_unreachable_thrift() throws TException, MalformedURLException { - databaseInstanceMock = mock(DatabaseInstance.class); + databaseInstanceMock = mock(DatabaseInstanceCloudant.class); when(databaseInstanceMock.checkIfDbExists(anyString())) .thenReturn(true); when(restHealthIndicatorMock.makeDatabaseInstance()) @@ -178,7 +178,7 @@ public void health_should_return_503_with_unreachable_thrift() throws TException @Test public void health_should_return_503_with_throwable() throws MalformedURLException { - final DatabaseInstance databaseInstanceMock = mock(DatabaseInstance.class); + final DatabaseInstanceCloudant databaseInstanceMock = mock(DatabaseInstanceCloudant.class); when(databaseInstanceMock.checkIfDbExists(anyString())) .thenThrow(new RuntimeException()); @@ -201,7 +201,7 @@ public void health_should_return_503_with_throwable() throws MalformedURLExcepti @Test public void health_should_return_200_when_healthy() throws TException, MalformedURLException { - databaseInstanceMock = mock(DatabaseInstance.class); + databaseInstanceMock = mock(DatabaseInstanceCloudant.class); when(databaseInstanceMock.checkIfDbExists(anyString())) .thenReturn(true); diff --git a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/DatabaseSanitationSpecTest.java b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/DatabaseSanitationSpecTest.java index 6f91678957..0888fd835a 100644 --- a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/DatabaseSanitationSpecTest.java +++ b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/DatabaseSanitationSpecTest.java @@ -31,9 +31,7 @@ import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.rest.resourceserver.TestHelper; import org.eclipse.sw360.rest.resourceserver.databasesanitation.Sw360DatabaseSanitationService; -import org.eclipse.sw360.rest.resourceserver.security.basic.Sw360CustomUserDetailsService; import org.eclipse.sw360.rest.resourceserver.security.basic.Sw360GrantedAuthority; -import org.eclipse.sw360.rest.resourceserver.user.Sw360UserService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,10 +41,9 @@ import org.springframework.hateoas.MediaTypes; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.cloudant.client.api.model.Attachment; +import com.ibm.cloud.cloudant.v1.model.Attachment; @RunWith(SpringJUnit4ClassRunner.class) public class DatabaseSanitationSpecTest extends TestRestDocsSpecBase { @@ -72,12 +69,14 @@ public void before() throws TException, IOException { Map responseMap = new HashMap<>(); List attachmentList = new ArrayList<>(); - attachment = new Attachment(); - attachment.setContentType("12345"); - attachment1 = new Attachment(); - attachment1.setContentType("23456"); - attachmentList.add(attachment.getContentType()); - attachmentList.add(attachment1.getContentType()); + attachment = new Attachment.Builder() + .contentType("12345") + .build(); + attachment1 = new Attachment.Builder() + .contentType("23456") + .build(); + attachmentList.add(attachment.contentType()); + attachmentList.add(attachment1.contentType()); List componentList = new ArrayList<>(); component = new Component(); From d79c8a63f5dea8ca0db6f028ca5b08e56f3a20d2 Mon Sep 17 00:00:00 2001 From: Gaurav Mishra Date: Thu, 12 Sep 2024 12:01:45 +0530 Subject: [PATCH 10/10] fix(nouveau): fix nouveau query result Signed-off-by: Gaurav Mishra --- .../main/java/org/eclipse/sw360/nouveau/CommonHits.java | 7 +++++++ .../sw360/nouveau/LuceneAwareCouchDbConnector.java | 8 ++++---- .../java/org/eclipse/sw360/nouveau/NouveauResult.java | 4 ++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CommonHits.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CommonHits.java index 45da7b7fe1..5451c8f604 100644 --- a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CommonHits.java +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CommonHits.java @@ -10,6 +10,8 @@ package org.eclipse.sw360.nouveau; +import com.google.gson.annotations.SerializedName; + import java.util.LinkedHashMap; import java.util.List; @@ -17,8 +19,13 @@ * Represents the Hits of a Lucene query. */ class CommonHits { + @SerializedName("order") protected List> order; + + @SerializedName("id") protected String id; + + @SerializedName("fields") protected LinkedHashMap fields; public List> getOrder() { diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java index 8bdea73f97..0a8940ce59 100644 --- a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java @@ -60,8 +60,8 @@ public NouveauAwareDatabase(@NotNull Cloudant client, String db, this.gson = gson; } - public ServiceCall queryNouveau(String index, @NotNull NouveauQuery query, - Class ignoredClassOfT) { + public ServiceCall queryNouveau(String index, + @NotNull NouveauQuery query) { Validator.notEmpty(index, "index cannot be empty"); Validator.notNull(query, "query cannot be null"); @@ -85,7 +85,7 @@ public ServiceCall queryNouveau(String index, @NotNull NouveauQuery query builder.header("Content-Type", "application/json"); builder.bodyContent(query.buildQuery(this.gson), "application/json"); - ResponseConverter responseConverter = ResponseConverterUtils.getValue((new TypeToken() { + ResponseConverter responseConverter = ResponseConverterUtils.getValue((new TypeToken() { }).getType()); return this.createServiceCall(builder.build(), responseConverter); } @@ -170,7 +170,7 @@ public boolean putNouveauDesignDocument( * @return The result of the query. */ public NouveauResult queryNouveau(String index, @NotNull NouveauQuery query) { - return this.database.queryNouveau(index, query, NouveauResult.class).execute().getResult(); + return this.database.queryNouveau(index, query).execute().getResult(); } /** diff --git a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/NouveauResult.java b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/NouveauResult.java index 06a254c691..4c031f1fd5 100644 --- a/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/NouveauResult.java +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/NouveauResult.java @@ -9,6 +9,8 @@ */ package org.eclipse.sw360.nouveau; +import com.google.gson.annotations.SerializedName; + import java.util.LinkedHashMap; import java.util.List; @@ -17,6 +19,7 @@ */ public class NouveauResult extends CommonNouveauResult { + @SerializedName("hits") private List hits; public List getHits() { @@ -29,6 +32,7 @@ public void setHits(List hits) { public static class Hits extends CommonHits { + @SerializedName("doc") private LinkedHashMap doc; /**