diff --git a/commons/com.b2international.commons/src/com/b2international/commons/exceptions/TooCostlyException.java b/commons/com.b2international.commons/src/com/b2international/commons/exceptions/TooCostlyException.java new file mode 100644 index 00000000000..4c3d2446a55 --- /dev/null +++ b/commons/com.b2international.commons/src/com/b2international/commons/exceptions/TooCostlyException.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 B2i Healthcare Pte Ltd, http://b2i.sg + * + * 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.b2international.commons.exceptions; + +/** + * @since 8.10 + */ +public final class TooCostlyException extends BadRequestException { + + private static final long serialVersionUID = 1L; + + public TooCostlyException(String developerMessage, Object...args) { + super("Request is too costly to execute. Alter your input parameters so that the system is able to process your request."); + withDeveloperMessage(developerMessage, args); + } + +} diff --git a/core/com.b2international.snowowl.core/src/com/b2international/snowowl/core/request/ecl/EclEvaluationRequest.java b/core/com.b2international.snowowl.core/src/com/b2international/snowowl/core/request/ecl/EclEvaluationRequest.java index 5865c14d7e4..ea7a6421c9f 100644 --- a/core/com.b2international.snowowl.core/src/com/b2international/snowowl/core/request/ecl/EclEvaluationRequest.java +++ b/core/com.b2international.snowowl.core/src/com/b2international/snowowl/core/request/ecl/EclEvaluationRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 B2i Healthcare Pte Ltd, http://b2i.sg + * Copyright 2022-2023 B2i Healthcare Pte Ltd, http://b2i.sg * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ import com.b2international.commons.CompareUtils; import com.b2international.commons.exceptions.BadRequestException; +import com.b2international.commons.exceptions.TooCostlyException; import com.b2international.index.query.*; import com.b2international.index.revision.RevisionSearcher; import com.b2international.snomed.ecl.Ecl; @@ -100,7 +101,11 @@ public void setIgnoredSyntaxErrorCodes(Set ignoredSyntaxErrorCodes) { } protected final Promise evaluate(C context, EObject expression) { - return dispatcher.invoke(context, expression); + try { + return dispatcher.invoke(context, expression); + } catch (StackOverflowError e) { + throw new TooCostlyException("ECL expression contains too many clauses and the server is unable to process it."); + } } protected Promise eval(C context, EObject eObject) { diff --git a/snomed/com.b2international.snowowl.snomed.datastore.tests/src/com/b2international/snowowl/snomed/core/ecl/SnomedEclEvaluationRequestTest.java b/snomed/com.b2international.snowowl.snomed.datastore.tests/src/com/b2international/snowowl/snomed/core/ecl/SnomedEclEvaluationRequestTest.java index 73282436fc2..3bfd729ba91 100644 --- a/snomed/com.b2international.snowowl.snomed.datastore.tests/src/com/b2international/snowowl/snomed/core/ecl/SnomedEclEvaluationRequestTest.java +++ b/snomed/com.b2international.snowowl.snomed.datastore.tests/src/com/b2international/snowowl/snomed/core/ecl/SnomedEclEvaluationRequestTest.java @@ -41,6 +41,7 @@ import org.junit.runners.Parameterized.Parameters; import com.b2international.commons.exceptions.BadRequestException; +import com.b2international.commons.exceptions.TooCostlyException; import com.b2international.index.query.Expression; import com.b2international.index.query.Expressions; import com.b2international.index.query.MatchNone; @@ -452,6 +453,19 @@ public void largeIdSetWithOrOperators() throws Exception { .collect(Collectors.joining(" OR "))); } + // XXX until we figure out how to process these effectively + @Test(expected = TooCostlyException.class) + public void largeEclOrOperatorOnly() throws Exception { + eval(IntStream.range(0, 10_000) + .mapToObj(i -> { + if (i % 10 == 0) { + return "<" + RandomSnomedIdentiferGenerator.generateConceptId(); + } else { + return RandomSnomedIdentiferGenerator.generateConceptId(); + } + }) + .collect(Collectors.joining(" OR "))); + } @Test(expected = BadRequestException.class) public void binaryOperatorAmbiguityOrAnd() throws Exception {