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/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..bae6c27950 100644 --- a/backend/src-common/pom.xml +++ b/backend/src-common/pom.xml @@ -63,5 +63,16 @@ com.github.package-url packageurl-java + + org.eclipse.sw360 + nouveau-handler + ${project.version} + compile + + + com.ibm.cloud + cloudant + ${cloudantsdk.version} + 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/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..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 @@ -18,11 +18,9 @@ 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.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.DocumentResult; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; import java.util.HashMap; import java.util.List; @@ -44,15 +42,17 @@ 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); } 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); } @@ -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..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 @@ -13,8 +13,9 @@ import org.eclipse.sw360.datahandler.cloudantclient.DatabaseRepositoryCloudantClient; import org.eclipse.sw360.datahandler.thrift.Source; -import com.cloudant.client.api.model.DesignDocument.MapReduce; -import com.cloudant.client.api.views.ViewRequestBuilder; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; +import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.List; @@ -31,13 +32,17 @@ 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); } - 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 f3b9322c83..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 @@ -13,8 +13,9 @@ 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.cloudant.client.api.views.ViewRequestBuilder; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; +import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.List; @@ -32,19 +33,27 @@ 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); } - 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 a828d3dba0..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 @@ -10,20 +10,17 @@ package org.eclipse.sw360.datahandler.db; -import com.cloudant.client.api.model.DesignDocument.MapReduce; -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.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; -import org.ektorp.support.View; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -55,7 +52,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")); @@ -67,34 +64,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(List.of(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(List.of(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(List.of(usedById, filter))) + .build(); + return queryView(viewQuery); } public List getUsagesByReleaseId(String releaseId) { @@ -102,50 +118,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 = 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) { - 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/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..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 @@ -18,12 +18,9 @@ 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.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.DesignDocumentViewsMapReduce; +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; @@ -161,7 +158,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)); @@ -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/ComponentSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentSearchHandler.java index 4115e97f21..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 @@ -9,26 +9,28 @@ */ 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.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.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.ektorp.http.HttpClient; - -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.ArrayList; 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; +import static org.eclipse.sw360.nouveau.LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX; /** * Class for accessing the Lucene connector on the CouchDB database @@ -40,75 +42,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); + public ComponentSearchHandler(Cloudant cClient, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); + Gson gson = db.getInstance().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 +106,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/ConfigContainerRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ConfigContainerRepository.java index bc4da43db7..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 @@ -14,11 +14,10 @@ import org.eclipse.sw360.datahandler.thrift.ConfigContainer; import org.eclipse.sw360.datahandler.thrift.ConfigFor; -import com.cloudant.client.api.model.DesignDocument.MapReduce; -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.DesignDocumentViewsMapReduce; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -30,7 +29,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)); @@ -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/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 9296a3dceb..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 @@ -9,76 +9,72 @@ */ package org.eclipse.sw360.datahandler.db; +import com.ibm.cloud.cloudant.v1.Cloudant; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +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 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; - 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); + 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(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); + Gson gson = db.getInstance().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..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 @@ -10,44 +10,60 @@ */ package org.eclipse.sw360.datahandler.db; +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.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.ektorp.http.HttpClient; - -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; 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 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});" + + " }" + + " }" + + "}")); - private final LuceneAwareDatabaseConnector connector; + private final NouveauLuceneAwareDatabaseConnector connector; - public ObligationElementSearchHandler(Supplier httpClient, 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 LuceneAwareDatabaseConnector(httpClient, cCLient, dbName); - connector.addView(luceneSearchView); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); + Gson gson = db.getInstance().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/ObligationListRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ObligationListRepository.java index 31a1ca7537..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 @@ -16,9 +16,8 @@ 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; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; /** * CRUD access for the Project class @@ -38,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 47a472d530..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 @@ -10,60 +10,55 @@ */ 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.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.ektorp.http.HttpClient; +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; 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(Cloudant 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, DDOC_NAME, dbName, db.getInstance().getGson()); + Gson gson = db.getInstance().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/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..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.cloudant.client.api.model.DesignDocument.MapReduce; -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.DesignDocumentViewsMapReduce; +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; @@ -54,7 +51,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)); @@ -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/PackageSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/PackageSearchHandler.java index 5dd24f1fdc..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 @@ -9,90 +9,86 @@ */ package org.eclipse.sw360.datahandler.db; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; +import com.ibm.cloud.cloudant.v1.Cloudant; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.packages.Package; -import org.ektorp.http.HttpClient; - -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.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; + private final NouveauLuceneAwareDatabaseConnector connector; - public PackageSearchHandler(Supplier httpClient, Supplier cloudantClient, String dbName) throws IOException { - connector = new LuceneAwareDatabaseConnector(httpClient, cloudantClient, dbName); - connector.addView(luceneSearchView); + public PackageSearchHandler(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); + Gson gson = db.getInstance().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/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..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 @@ -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; @@ -28,27 +26,21 @@ import org.eclipse.sw360.datahandler.thrift.users.UserGroup; import org.jetbrains.annotations.NotNull; -import com.cloudant.client.api.model.DesignDocument.MapReduce; -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.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.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; /** @@ -266,7 +258,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)); @@ -396,115 +388,94 @@ 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()); - ViewRequestBuilder queryView = null; + 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().createQuery(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) { - 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(); + 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/ProjectSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java index 03c49e1446..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 @@ -9,101 +9,101 @@ */ package org.eclipse.sw360.datahandler.db; +import com.ibm.cloud.cloudant.v1.Cloudant; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; 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.ektorp.http.HttpClient; - -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.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(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); + Gson gson = db.getInstance().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 +114,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 +130,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/ProjectVulnerabilityRatingRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectVulnerabilityRatingRepository.java index 629201caff..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 @@ -16,9 +16,8 @@ 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; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; import java.util.HashMap; import java.util.List; @@ -59,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..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; @@ -17,10 +18,8 @@ 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.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.DesignDocumentViewsMapReduce; +import com.ibm.cloud.cloudant.v1.model.PostViewOptions; /** * CRUD access for the RelationsUsageRepository class @@ -42,15 +41,15 @@ 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); } 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 aee6803f4b..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 @@ -19,10 +19,9 @@ import org.eclipse.sw360.datahandler.thrift.PaginationData; import java.util.*; -import com.cloudant.client.api.model.DesignDocument.MapReduce; -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.DesignDocumentViewsMapReduce; +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 @@ -169,7 +166,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)); @@ -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/ReleaseSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ReleaseSearchHandler.java index 7e248f14a4..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 @@ -9,18 +9,20 @@ */ package org.eclipse.sw360.datahandler.db; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; +import com.ibm.cloud.cloudant.v1.Cloudant; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.components.Release; -import org.ektorp.http.HttpClient; - -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 Release class @@ -29,26 +31,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 final LuceneAwareDatabaseConnector connector; - - public ReleaseSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { - connector = new LuceneAwareDatabaseConnector(httpClient, cClient, dbName); - connector.addView(luceneSearchView); + 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 NouveauLuceneAwareDatabaseConnector connector; + + public ReleaseSearchHandler(Cloudant cClient, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(cClient, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); + Gson gson = db.getInstance().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/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 aaf83b4f67..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; @@ -20,19 +21,14 @@ 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.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.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.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; @@ -120,7 +116,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)); @@ -194,12 +190,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; @@ -211,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-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..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 @@ -9,85 +9,81 @@ */ 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 com.ibm.cloud.cloudant.v1.Cloudant; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.users.User; - -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.Map; 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(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, 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, DDOC_NAME, dbName, db.getInstance().getGson()); + Gson gson = db.getInstance().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 +93,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/VendorRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/VendorRepository.java index c7902c34b1..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; @@ -25,7 +24,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 +50,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 c03689fa05..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 @@ -9,18 +9,20 @@ */ 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 com.ibm.cloud.cloudant.v1.Cloudant; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +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 +33,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 { + public VendorSearchHandler(Cloudant client, String dbName) throws IOException { // Creates the database connector and adds the lucene search view - connector = new LuceneAwareDatabaseConnector(databaseConnector, cClient); - connector.addView(luceneSearchView); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); + Gson gson = db.getInstance().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/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 f440bf53a2..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 @@ -10,39 +10,48 @@ */ 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.ibm.cloud.cloudant.v1.Cloudant; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; -import org.ektorp.http.HttpClient; - -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; 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 final LuceneAwareDatabaseConnector connector; - - public SpdxDocumentSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { - connector = new LuceneAwareDatabaseConnector(httpClient, cClient, dbName); - connector.addView(luceneSearchView); + 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 NouveauLuceneAwareDatabaseConnector connector; + + public SpdxDocumentSearchHandler(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); + Gson gson = db.getInstance().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/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 d863e38f6f..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 @@ -10,39 +10,48 @@ */ 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.ibm.cloud.cloudant.v1.Cloudant; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.couchdb.lucene.NouveauLuceneAwareDatabaseConnector; import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; -import org.ektorp.http.HttpClient; - -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; 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 final LuceneAwareDatabaseConnector connector; - - public SpdxDocumentCreationInfoSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { - connector = new LuceneAwareDatabaseConnector(httpClient, cClient, dbName); - connector.addView(luceneSearchView); + 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 NouveauLuceneAwareDatabaseConnector connector; + + public SpdxDocumentCreationInfoSearchHandler(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); + Gson gson = db.getInstance().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/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 70b5b2d61f..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 @@ -10,39 +10,48 @@ */ 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.ibm.cloud.cloudant.v1.Cloudant; +import com.google.gson.Gson; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +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 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 final LuceneAwareDatabaseConnector connector; - - public SpdxPackageInfoSearchHandler(Supplier httpClient, Supplier cClient, String dbName) throws IOException { - connector = new LuceneAwareDatabaseConnector(httpClient, cClient, dbName); - connector.addView(luceneSearchView); + 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 NouveauLuceneAwareDatabaseConnector connector; + + public SpdxPackageInfoSearchHandler(Cloudant client, String dbName) throws IOException { + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, dbName); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); + Gson gson = db.getInstance().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-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 cecd8b62a6..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 @@ -24,16 +24,14 @@ 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; +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.*; @@ -50,24 +48,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(Cloudant 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(Cloudant 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/BulkDeleteUtilTest.java b/backend/src/src-components/src/test/java/org/eclipse/sw360/components/db/BulkDeleteUtilTest.java index e91ac2723a..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,9 +12,9 @@ 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.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.db.ComponentDatabaseHandler; import org.eclipse.sw360.datahandler.db.BulkDeleteUtil; import org.eclipse.sw360.datahandler.entitlement.ComponentModerator; @@ -97,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; @@ -127,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 b0760eeea3..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,9 +15,9 @@ 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.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.db.ComponentDatabaseHandler; import org.eclipse.sw360.datahandler.db.SvmConnector; import org.eclipse.sw360.datahandler.entitlement.ComponentModerator; @@ -141,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 23e1bd44e5..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,9 +12,8 @@ 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.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; @@ -77,14 +76,14 @@ 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); } // Prepare the handler - searchHandler = new ComponentSearchHandler(DatabaseSettingsTest.getConfiguredHttpClient(), DatabaseSettingsTest.getConfiguredClient(), dbName); + searchHandler = new ComponentSearchHandler(DatabaseSettingsTest.getConfiguredClient(), dbName); } @After 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-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/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-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 c9dc8cac5f..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 @@ -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; @@ -23,17 +24,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; import java.io.IOException; import static org.eclipse.sw360.datahandler.common.SW360Assert.*; @@ -49,14 +45,14 @@ 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 { - 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 e4277b3f34..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,9 +17,7 @@ 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; +import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce; /** * CRUD access for the LicenseObligationList class @@ -39,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/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-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..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.cloudant.client.api.model.DesignDocument.MapReduce; -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.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.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.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 * @@ -83,7 +78,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")); @@ -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; @@ -245,25 +237,26 @@ 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); } 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); } @@ -292,21 +285,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 +313,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 +334,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/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..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,8 +10,8 @@ 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.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; @@ -26,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-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..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 @@ -41,14 +41,12 @@ 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; +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 @@ -67,17 +65,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); + ProjectHandler(Cloudant client, String dbName, String attchmntDbName) throws IOException { + handler = new ProjectDatabaseHandler(client, dbName, attchmntDbName); + searchHandler = new ProjectSearchHandler(DatabaseSettings.getConfiguredClient(), dbName); } - ProjectHandler(Supplier cClient,Supplier hClient, String dbName, String changeLogsDbName, String attchmntDbName) throws IOException { - handler = new ProjectDatabaseHandler(cClient, dbName, changeLogsDbName, attchmntDbName); - searchHandler = new ProjectSearchHandler(hClient, 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 3534e2e938..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,9 +12,9 @@ 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.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.db.AttachmentDatabaseHandler; import org.eclipse.sw360.datahandler.db.ComponentDatabaseHandler; import org.eclipse.sw360.datahandler.db.PackageDatabaseHandler; @@ -69,13 +69,13 @@ 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); } // 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..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,9 +10,9 @@ */ package org.eclipse.sw360.search; -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; @@ -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; @@ -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; @@ -55,9 +53,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(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 09fad9dd42..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 @@ -13,12 +13,10 @@ 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; +import com.ibm.cloud.cloudant.v1.Cloudant; import java.io.IOException; -import java.util.function.Supplier; public class Sw360usersDatabaseSearchHandler extends AbstractDatabaseSearchHandler { @@ -26,8 +24,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(Cloudant 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 32e528bae4..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 @@ -10,24 +10,29 @@ */ package org.eclipse.sw360.search.db; -import com.cloudant.client.api.CloudantClient; -import com.github.ldriscoll.ektorplucene.LuceneResult; +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.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.ektorp.http.HttpClient; +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.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 +41,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 { + Cloudant client = DatabaseSettings.getConfiguredClient(); + DatabaseConnectorCloudant db = new DatabaseConnectorCloudant(client, 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, DDOC_NAME, dbName, db.getInstance().getGson()); + Gson gson = db.getInstance().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(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 LuceneAwareDatabaseConnector(client, cclient, dbName); - connector.addView(luceneSearchView); - connector.addView(luceneFilteredSearchView); + connector = new NouveauLuceneAwareDatabaseConnector(db, DDOC_NAME, dbName, db.getInstance().getGson()); + Gson gson = db.getInstance().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 +178,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 +206,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 +222,4 @@ private static SearchResult makeSearchResult(LuceneResult.Row row) { return result; } - } 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-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..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 @@ -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,12 +25,10 @@ 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; +import com.ibm.cloud.cloudant.v1.Cloudant; import java.io.IOException; -import java.util.function.Supplier; public class Sw360dbDatabaseSearchHandler extends AbstractDatabaseSearchHandler { @@ -52,10 +49,10 @@ 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(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-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-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 0d135e339a..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; @@ -26,13 +26,10 @@ 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; import static org.eclipse.sw360.datahandler.common.SW360Assert.assertNotEmpty; import static org.eclipse.sw360.datahandler.common.SW360Assert.assertNotNull; @@ -56,8 +53,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(Cloudant 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 c9f4ff6d9b..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.*; @@ -32,13 +31,11 @@ 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; 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; @@ -56,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); @@ -69,21 +65,12 @@ 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(dbConnector, httpClient); - } - - 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(DatabaseSettings.getConfiguredClient(), dbName); } public User getByEmail(String 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/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..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,8 +9,8 @@ */ package org.eclipse.sw360.vendors; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; 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; @@ -31,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 5b86901af7..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,9 +10,8 @@ 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.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; @@ -40,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")); @@ -50,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 afd7c939a6..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,12 +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.DatabaseConnector; -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; /** @@ -17,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) {" + @@ -41,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 1b3dcaeab6..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,12 +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.DatabaseConnector; -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; /** @@ -17,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) {" + @@ -62,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/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..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,14 +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.DatabaseConnector; -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; /** @@ -19,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) {" + @@ -36,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 7234aa7680..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,12 +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.DatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.DatabaseRepository; -import org.ektorp.support.View; +import java.util.HashMap; +import java.util.Map; import java.util.Set; /** @@ -17,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) {" + @@ -41,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 72196380bc..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,12 +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.DatabaseConnector; -import org.eclipse.sw360.datahandler.couchdb.DatabaseRepository; -import org.ektorp.support.View; +import java.util.HashMap; +import java.util.Map; import java.util.Set; /** @@ -17,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) {" + @@ -27,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 a22ca7ad2a..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,9 +6,9 @@ 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.couchdb.DatabaseConnector; import org.eclipse.sw360.datahandler.thrift.RequestStatus; import org.eclipse.sw360.datahandler.thrift.vmcomponents.*; @@ -59,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"); @@ -92,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/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..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.DatabaseConnector; -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) {" + @@ -48,26 +55,28 @@ 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 faec8792bd..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.DatabaseConnector; -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) {" + @@ -56,23 +63,31 @@ public class VulnerabilityRepository extends DatabaseRepository { " } " + "}"; - public VulnerabilityRepository(DatabaseConnector db) { - super(Vulnerability.class, db); + public VulnerabilityRepository(DatabaseConnectorCloudant 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/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 b130b7f28f..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 @@ -15,20 +15,17 @@ 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; +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; @@ -47,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/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 c89ab01a55..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 @@ -188,14 +189,6 @@ org.jetbrains annotations - - com.github.ldriscoll - ektorplucene - - - org.ektorp - org.ektorp - commons-io commons-io @@ -207,8 +200,9 @@ ${gson.version} - com.cloudant - cloudant-client + com.ibm.cloud + cloudant + ${cloudantsdk.version} com.fasterxml.jackson.core @@ -222,10 +216,6 @@ com.fasterxml.jackson.core jackson-annotations - - com.github.stephenc.findbugs - findbugs-annotations - org.apache.thrift libthrift @@ -266,5 +256,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..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 @@ -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,56 @@ */ 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.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; + 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; 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,38 +66,32 @@ 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"); - } + if (document instanceof AttachmentContent content) { + InputStream in = getAttachment(content.getId(), content.getFilename()); + resp = this.updateWithResponse(document); + createAttachment(resp.getId(), content.getFilename(), in, content.getContentType()); } else { - resp = database.update(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()); + resp = this.updateWithResponse(document); } + updateIdAndRev(document, resp.getId(), resp.getRev()); } else { log.warn("Ignore updating a null 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; } @@ -113,24 +100,39 @@ public DatabaseInstanceCloudant getInstance() { return instance; } - public Set getAllIds(Class type) { + public Set getAllIds(@NotNull 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; } + /** + * 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 { - T obj = (T) database.find(type, id); + Document doc = getDocument(id); + T obj = this.getPojoFromDocument(doc, type); + String extractedType = null; Field[] f = obj.getClass().getDeclaredFields(); for (Field fi : f) { @@ -141,16 +143,39 @@ 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; } } 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; } } @@ -158,8 +183,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,12 +201,100 @@ public List getAll(Class type) { } public boolean remove(String id) { - Response resp = database.remove(id); - boolean success = resp.getStatusCode() == HttpStatus.SC_OK ? true : false; - if (!success) { - log.error("Could not delete document with id: " + id); + if (!contains(id)) { + return false; + } + DeleteDocumentOptions deleteOption = new DeleteDocumentOptions.Builder() + .db(this.dbName) + .docId(id) + .build(); + + return deleteDocumentWithOption(deleteOption); + } + + 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(); + } + } + + /** + * 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 (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); } - return success; } public List get(Class type, Collection ids) { @@ -180,14 +302,22 @@ public List get(Class type, Collection 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 -> { + 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(); } @@ -197,19 +327,23 @@ 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(@NotNull 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); - TFieldIdEnum id = tbase.fieldForId(1); - TFieldIdEnum rev = tbase.fieldForId(2); - tbase.setFieldValue(id, responses.get(i).getId()); - tbase.setFieldValue(rev, responses.get(i).getRev()); - } + responses = this.instance.getClient().postBulkDocs(bulkDocsOptions).execute().getResult(); + for (int i = 0; i < entities.length; i++) { + updateIdAndRev(entities[i], responses.get(i).getId(), responses.get(i).getRev()); } } catch (Exception e) { log.error("Error in bulk execution", e); @@ -218,98 +352,264 @@ 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(@NotNull 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) { + public int getDocumentCount(@NotNull 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(@NotNull 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; } - 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) { - 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 (!contains(id)) { + return false; + } + + Document doc; + try { + doc = getDocument(id); + } catch (SW360Exception e) { + return false; } - return result.getStatusCode() == HttpStatus.SC_OK ? true : false; + 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) { - Response resp = database.save(doc); - 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()); + Document document = this.getDocumentFromPojo(doc); + if (document.getId() != null && this.contains(document.getId())) { + // Cannot add same document again. Must update. + return false; } - return resp.getStatusCode() == HttpStatus.SC_CREATED ? true : false; + PostDocumentOptions postDocOption = new PostDocumentOptions.Builder() + .db(this.dbName) + .document(document) + .build(); + + DocumentResult resp = this.instance.getClient().postDocument(postDocOption).execute().getResult(); + updateIdAndRev(doc, resp.getId(), resp.getRev()); + 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 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(ddoc) + .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(), ddoc, response.getError()); + } else { + designDocument.setId(response.getId()); + designDocument.setRev(response.getRev()); + } + return success; } - public DesignDocumentManager getDesignDocumentManager() { - return database.getDesignDocumentManager(); + 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() + .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 void createIndex(String indexDefinition) { - database.createIndex(indexDefinition); + public PostFindOptions.Builder getQueryBuilder() { + return new PostFindOptions.Builder().db(this.dbName); } - public QueryResult getQueryResult(String query, Class type) { - return database.query(query, type); + 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(Class type, String viewName) { - ViewRequest countReq1 = database.getViewRequestBuilder(type.getSimpleName(), viewName) - .newRequest(Key.Type.STRING, String.class).includeDocs(false).build(); + public Set getDistinctSortedStringKeys(@NotNull Class type, String viewName) { + 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,14 +619,139 @@ 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 Document getDocumentFromPojo(Object document) { + if (document instanceof Document) { + return (Document) document; + } + Document doc = new Document(); + Gson gson = this.instance.getGson(); + Type t = new TypeToken>() {}.getType(); + 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) { + T doc = this.instance.getGson().fromJson(document.toString(), type); + updateIdAndRev(doc, document.getId(), document.getRev()); + return doc; + } + + 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(); + + try { + return this.instance.getClient().headDocument(documentOptions).execute() + .getStatusCode() == 200; + } catch (NotFoundException e) { + return false; + } + } + + /** + * 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)); + } } 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..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,25 +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 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..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 @@ -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; @@ -20,24 +21,24 @@ 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; -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.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 @@ -49,35 +50,36 @@ public class DatabaseRepositoryCloudantClient { private static final char HIGH_VALUE_UNICODE_CHARACTER = '\uFFF0'; private final Class type; - private final DatabaseConnectorCloudant connector; - - public void initStandardDesignDocument(Map views, DatabaseConnectorCloudant db) { - DesignDocument newDdoc = new DesignDocument(); - String ddocId = "_design/" + type.getSimpleName(); - newDdoc.setId(ddocId); - 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); - DesignDocumentManager ddocManager = db.getDesignDocumentManager(); - ddocManager.put(ddocFinal); + private DatabaseConnectorCloudant connector; + + public void initStandardDesignDocument(Map views, + @NotNull DatabaseConnectorCloudant db) { + String ddocId = type.getSimpleName(); + DesignDocument newDdoc = new DesignDocument.Builder() + .views(views) + .build(); + db.putDesignDocument(newDdoc, 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() { @@ -85,14 +87,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) { @@ -100,87 +99,95 @@ 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) { - 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 +206,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 +252,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(); + public ViewResult queryViewForComplexKeys(PostViewOptions req) { + ViewResult responses = null; 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; - 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) { @@ -370,11 +359,17 @@ 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); } 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); } @@ -391,7 +386,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 +397,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..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,10 +11,10 @@ */ package org.eclipse.sw360.datahandler.common; -import com.cloudant.client.api.model.Response; 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 +560,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 80e41e3dc6..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 @@ -37,7 +32,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 +59,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"); @@ -78,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/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/AttachmentConnector.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/AttachmentConnector.java index 50887f21da..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 @@ -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; @@ -37,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 @@ -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/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/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/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..5f1077dcd7 --- /dev/null +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/lucene/NouveauLuceneAwareDatabaseConnector.java @@ -0,0 +1,327 @@ +/* + * 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.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.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 dbClient, + String ddoc, String db, Gson gson) throws IOException { + super(dbClient.getInstance().getClient(), ddoc, db, gson); + setResultLimit(DatabaseSettings.LUCENE_SEARCH_LIMIT); + this.connector = dbClient; + } + + /** + * 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); + } + + if (!designDocument.equals(documentFromDb)) { + 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) -> { + if (! designDocument.getNouveau().has(key)) { + designDocument.getNouveau().add(key, value); + } + }); + } + return putNouveauDesignDocument(designDocument); + } + 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 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/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..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 @@ -14,8 +14,11 @@ 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 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,8 +43,9 @@ 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.lang.reflect.Type; import java.util.Collection; import java.util.List; import java.util.Map; @@ -99,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); @@ -117,19 +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 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,22 @@ public static , FS extends TFieldIdEnum, D extends TBase< } public static Map getIdMap(Collection in) { - return Maps.uniqueIndex(in, Documents::getId); + return Maps.uniqueIndex(in, value -> { + if (value instanceof Document doc) { + return doc.getId(); + } + Gson gson = getGson(); + Type t = new TypeToken>() {}.getType(); + + 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 ""; + }); } public static , F extends TFieldIdEnum> Function extractField(final F field) { @@ -176,4 +185,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/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 eedc3dc46d..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 @@ -9,20 +9,15 @@ */ 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 org.jetbrains.annotations.NotNull; import java.net.MalformedURLException; -import java.net.URL; import java.util.Properties; -import java.util.function.Supplier; /** * Constants for the database address @@ -33,7 +28,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 +42,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", ""); @@ -59,39 +52,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 @NotNull Cloudant getConfiguredClient() { + Cloudant client; + if (!COUCH_DB_USERNAME.isEmpty() && !COUCH_DB_PASSWORD.isEmpty()) { + 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; } @@ -99,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/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/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 2f453e80e3..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 @@ -81,8 +81,8 @@ 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); - VendorHandler vendorHandler = new VendorHandler(DatabaseSettingsTest.getConfiguredClient(), DatabaseSettingsTest.getConfiguredHttpClient(), DatabaseSettingsTest.COUCH_DB_DATABASE); + 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.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 new file mode 100644 index 0000000000..7061051b4e --- /dev/null +++ b/libraries/nouveau-handler/pom.xml @@ -0,0 +1,195 @@ + + + + 4.0.0 + + + libraries + org.eclipse.sw360 + 18.99.1 + + + nouveau-handler + nouveau-handler + jar + + + ${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} + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + + test-jar + + + + + + + true + org.eclipse.sw360.nouveau + + + + + + 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.ibm.cloud + cloudant + ${cloudantsdk.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..5451c8f604 --- /dev/null +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/CommonHits.java @@ -0,0 +1,58 @@ +/* + * 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.util.LinkedHashMap; +import java.util.List; + +/** + * 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() { + 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..0a8940ce59 --- /dev/null +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/LuceneAwareCouchDbConnector.java @@ -0,0 +1,219 @@ +/* + * 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.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; + +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/' not needed with Cloudant SDK + + private final NouveauAwareDatabase database; + + 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 extends Cloudant { + private final String db; + private final String ddoc; + private final String lucenePrefix; + private final Gson gson; + + 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; + this.gson = gson; + } + + public ServiceCall queryNouveau(String index, + @NotNull NouveauQuery query) { + 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}/_design/{ddoc}/{nouveauPrefix}/{index}", + pathParamsMap)); + Map sdkHeaders = SdkCommon.getSdkHeaders("cloudant", "v1", "postNouveauQuery"); + + for (Map.Entry stringStringEntry : sdkHeaders.entrySet()) { + builder.header(stringStringEntry.getKey(), stringStringEntry.getValue()); + } + + builder.header("Accept", "application/json"); + builder.header("Content-Type", "application/json"); + + builder.bodyContent(query.buildQuery(this.gson), "application/json"); + ResponseConverter responseConverter = ResponseConverterUtils.getValue((new TypeToken() { + }).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; + } + } + + /** + * 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).execute().getResult(); + } + + /** + * 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 (!DEFAULT_DESIGN_PREFIX.isEmpty() && !designId.startsWith(DEFAULT_DESIGN_PREFIX)) { + return DEFAULT_DESIGN_PREFIX + designId; + } else { + 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/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..4c031f1fd5 --- /dev/null +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/NouveauResult.java @@ -0,0 +1,49 @@ +/* + * 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.util.LinkedHashMap; +import java.util.List; + +/** + * The result of the lucene query. + */ +public class NouveauResult extends CommonNouveauResult { + + @SerializedName("hits") + private List hits; + + public List getHits() { + return hits; + } + + public void setHits(List hits) { + this.hits = hits; + } + + public static class Hits extends CommonHits { + + @SerializedName("doc") + 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..39c866696d --- /dev/null +++ b/libraries/nouveau-handler/src/main/java/org/eclipse/sw360/nouveau/designdocument/NouveauDesignDocument.java @@ -0,0 +1,62 @@ +/* + * 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.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; + +import java.util.Objects; + +/** + * CouchDB Design document that contains nouveau indexes. + */ +public class NouveauDesignDocument extends DesignDocument { + + @SerializedName("nouveau") + 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..e9c5776068 --- /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 (LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX + "lucene").equals(LuceneAwareCouchDbConnector.ensureDesignId("lucene")); + } + + public void testEnsureDesignIdContaining() { + assert (LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX + "lucene").equals(LuceneAwareCouchDbConnector.ensureDesignId(LuceneAwareCouchDbConnector.DEFAULT_DESIGN_PREFIX + "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/pom.xml b/pom.xml index f6faf5bdd9..b248d273c2 100644 --- a/pom.xml +++ b/pom.xml @@ -90,7 +90,7 @@ 2.14.2 1.12.13 - 2.19.1 + 0.9.1 1.15 4.4 1.21 @@ -100,8 +100,6 @@ 3.12.0 1.10.0 2.1.3 - 1.5.0 - 0.2.0 1.0.1 1.3.9-1 2.3.9 @@ -121,7 +119,6 @@ 4.13.2 3.6.2 2.19.0 - 6.6.6 4.7.0 4.10.0 1.1.1 @@ -300,28 +297,6 @@ commons-collections4 ${commons-collection4.version} - - com.github.ldriscoll - ektorplucene - ${ektorplucene.version} - - - net.sourceforge.findbugs - annotations - - - - - - com.github.stephenc.findbugs - findbugs-annotations - ${findbugs-annotations.version} - - - org.ektorp - org.ektorp - ${ektorp.version} - commons-io commons-io @@ -347,11 +322,6 @@ httpcore ${httpcore.version} - - org.apache.httpcomponents.client5 - httpclient5 - ${httpclient5.version} - javax.activation activation @@ -420,9 +390,9 @@ test - com.cloudant - cloudant-client - ${cloudant.version} + com.ibm.cloud + cloudant + ${cloudantsdk.version} 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 - - - 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/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/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/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); } 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(); 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/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/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 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 9bce480ddc..0000000000 Binary files a/third-party/couchdb-lucene/src/test/resources/paxos-simple.pdf and /dev/null differ 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