Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug 565011 rebuild the core of condition expression evaluation #298

Merged
merged 3 commits into from
Jul 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
package org.eclipse.passage.lic.internal.api.conditions.evaluation;

import java.util.Collection;
import java.util.Collections;
import java.util.Objects;

import org.eclipse.passage.lic.internal.api.diagnostic.FailureDiagnostic;

/**
* Report {@linkplain Condition}s evaluation results.
*
Expand Down Expand Up @@ -45,7 +48,7 @@ public interface Emission {

Collection<Permission> permissions();

EmissionFailureDiagnostic failureDiagnostic();
FailureDiagnostic failureDiagnostic();

public static final class Successful implements Emission {

Expand All @@ -56,6 +59,10 @@ public Successful(Collection<Permission> permissions) {
this.permissions = permissions;
}

public Successful(Permission permission) {
this(Collections.singleton(permission));
}

@Override
public boolean failed() {
return false;
Expand All @@ -67,17 +74,17 @@ public Collection<Permission> permissions() {
}

@Override
public EmissionFailureDiagnostic failureDiagnostic() {
public FailureDiagnostic failureDiagnostic() {
throw new UnsupportedOperationException();
}

}

public static final class Failed implements Emission {

private final EmissionFailureDiagnostic diagnose;
private final FailureDiagnostic diagnose;

public Failed(EmissionFailureDiagnostic diagnose) {
public Failed(FailureDiagnostic diagnose) {
Objects.requireNonNull(diagnose, "Emission.Failed::diagnose"); //$NON-NLS-1$
this.diagnose = diagnose;
}
Expand All @@ -93,7 +100,7 @@ public Collection<Permission> permissions() {
}

@Override
public EmissionFailureDiagnostic failureDiagnostic() {
public FailureDiagnostic failureDiagnostic() {
return diagnose;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
*******************************************************************************/
package org.eclipse.passage.lic.internal.api.conditions.evaluation;

/**
* <p>
* FIXME: diagnostic details structure are yet to be designed as existing
* emission process is reimplemented on top of these interfaces. Do we need try
* to evaluate the rest of the condition after the first failure to get the full
* picture? Or this first failure is enough? The answer will affect further
* development of this interface.
* </p>
*/
public interface EmissionFailureDiagnostic {
@SuppressWarnings("serial")
public final class ExpressionEvaluationException extends Exception {

public ExpressionEvaluationException(String message, Throwable cause) {
super(message, cause);
}

public ExpressionEvaluationException(String message) {
super(message);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@

public interface ExpressionEvaluationService extends Service<ExpressionProtocol> {

boolean evaluate(ParsedExpression expression, ExpressionTokenAssessmentService assessor);
/**
* Assess the {@code expression} tokens with the given {@code assessor}.
*
* @throws ExpressionEvaluationException in case of infrastructure denial or
* assessment failure
*/
void evaluate(ParsedExpression expression, ExpressionTokenAssessmentService assessor)
throws ExpressionEvaluationException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import org.eclipse.passage.lic.internal.api.registry.Service;

public interface ExpressionPasringService extends Service<ExpressionProtocol> {
public interface ExpressionParsingService extends Service<ExpressionProtocol> {

ParsedExpression parsed(String expression) throws ExpressionParsingException;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@

import org.eclipse.passage.lic.internal.api.registry.Registry;

public interface ExpressionPasringRegistry extends Supplier<Registry<ExpressionProtocol, ExpressionPasringService>> {
public interface ExpressionPasringRegistry extends Supplier<Registry<ExpressionProtocol, ExpressionParsingService>> {

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,20 @@
* must answer only these simple questions. the rest of the evaluation logic is
* on {@linkplain ExpressionProtocol}-aware services.
* </p>
* <p>
* This interface is to be extended further to cover all atomic queries we might
* need.
* </p>
*/
public interface ExpressionTokenAssessmentService extends Service<EvaluationType> {

boolean equal(String key, String value);

// contains ()

// startsWith ()

// all the things we will ever need -> slice to [Operation]s
/**
* Assess dedicated part of the runtime environment is the given property
* {@code key} equals to the expected {@code value}.
*
* @throws ExpressionEvaluationException in the case of any evaluation
* infrastructure denial or misbehavior
*/
boolean equal(String key, String value) throws ExpressionEvaluationException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
* Contributors:
* ArSysOp - initial API and implementation
*******************************************************************************/
package org.eclipse.passage.lic.internal.base.conditions.evaluation;
package org.eclipse.passage.lic.internal.api.diagnostic;

import org.eclipse.passage.lic.internal.api.conditions.evaluation.EmissionFailureDiagnostic;
import java.util.List;

public interface FailureDiagnostic {

List<Trouble> troubles();

@SuppressWarnings("restriction")
final class BaseEmissionFailureDiagnostic implements EmissionFailureDiagnostic {
// FIXME: ytbd
}
1 change: 1 addition & 0 deletions bundles/org.eclipse.passage.lic.base/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Export-Package: org.eclipse.passage.lic.base,
org.eclipse.passage.lic.internal.base.conditions;x-internal:=true,
org.eclipse.passage.lic.internal.base.conditions.evaluation;x-internal:=true,
org.eclipse.passage.lic.internal.base.conditions.mining;x-internal:=true,
org.eclipse.passage.lic.internal.base.diagnostic;x-internal:=true,
org.eclipse.passage.lic.internal.base.i18n;x-internal:=true,
org.eclipse.passage.lic.internal.base.io;x-internal:=true,
org.eclipse.passage.lic.internal.base.permission;x-internal:=true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.eclipse.passage.lic.api.conditions.LicensingCondition;
import org.eclipse.passage.lic.base.BaseLicensingResult;
import org.eclipse.passage.lic.base.LicensingResults;
import org.eclipse.passage.lic.internal.base.conditions.evaluation.AndsProtocolExpressionParseService;
import org.eclipse.passage.lic.internal.base.i18n.BaseMessages;

public class LicensingConditions {
Expand All @@ -33,10 +34,13 @@ private LicensingConditions() {
// block
}

/**
* @deprecated use {@link AndsProtocolExpressionParseService}
*/
@Deprecated
public static Map<String, String> parseExpression(String expression) {
Map<String, String> map = new HashMap<>();
if (expression == null) {
// FIXME: report error;
return map;
}
String[] segments = expression.split(SEGMENT_SEPARATOR);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,20 @@
import java.util.Objects;

import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionParsingException;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionPasringService;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionParsingService;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionProtocol;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ParsedExpression;
import org.eclipse.passage.lic.internal.base.i18n.ConditionsEvaluationMessages;

/**
*
*/
@SuppressWarnings("restriction")
public final class AndsProtocolExpressionParseService implements ExpressionPasringService {
public final class AndsProtocolExpressionParseService implements ExpressionParsingService {

private final ExpressionProtocol protocol = new ExpressionProtocol.Ands();
private final String separator = ";"; //$NON-NLS-1$
private final String mediator = "="; //$NON-NLS-1$

@Override
public ExpressionProtocol id() {
Expand All @@ -37,17 +40,35 @@ public ExpressionProtocol id() {
/**
* Expect the incoming {@code expression} to be a semicolon-separated
* {@code key=value} pairs meaning {@code AND}-ed equality checks.
* <p>
* FIXME: contract: <no checks> situation must cause failure - cover it with a
* contract test
* </p>
*/
@Override
public ParsedExpression parsed(String expression) throws ExpressionParsingException {
Objects.requireNonNull(expression);
Map<String, String> checks = new HashMap<>();
// FIXME: ytbd: do further work: split and fill the map
return new SimpleMapExpression(protocol, checks);
Map<String, String> couples = new HashMap<>();
for (String segment : expression.split(separator)) {
addCouple(segment, couples);
}
if (couples.isEmpty()) {
throw new ExpressionParsingException(String.format(//
ConditionsEvaluationMessages.getString("AndsProtocolExpressionParseService.no_checks"), //$NON-NLS-1$
expression));
}
return new SimpleMapExpression(protocol, couples);
}

private void addCouple(String segment, Map<String, String> couples) throws ExpressionParsingException {
String[] couple = segment.split(mediator);
if (coupleIsInvalid(couple)) {
throw new ExpressionParsingException(String.format(//
ConditionsEvaluationMessages.getString("AndsProtocolExpressionParseService.invalid_format"), segment)); //$NON-NLS-1$
}
couples.put(couple[0].trim(), couple[1].trim());
}

private boolean coupleIsInvalid(String[] couple) {
return (couple.length != 2) || //
couple[0].trim().isEmpty() || //
couple[1].trim().isEmpty();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.eclipse.passage.lic.internal.api.conditions.ValidityPeriod;
import org.eclipse.passage.lic.internal.api.conditions.ValidityPeriodClosed;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.Emission;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionEvaluationException;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionEvaluationService;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionEvaluatorsRegistry;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionParsingException;
Expand All @@ -33,7 +34,12 @@
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionTokenAssessorsRegistry;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ParsedExpression;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.PermissionEmittingService;
import org.eclipse.passage.lic.internal.api.diagnostic.code.LicenseCheckFailed;
import org.eclipse.passage.lic.internal.api.diagnostic.code.LicenseDoesNotMatch;
import org.eclipse.passage.lic.internal.api.diagnostic.code.LicenseInvalid;
import org.eclipse.passage.lic.internal.api.registry.StringServiceId;
import org.eclipse.passage.lic.internal.base.diagnostic.BaseFailureDiagnostic;
import org.eclipse.passage.lic.internal.base.i18n.ConditionsEvaluationMessages;

@SuppressWarnings("restriction")
public final class BasePermissionEmittingService implements PermissionEmittingService {
Expand Down Expand Up @@ -70,34 +76,45 @@ public Emission emit(Collection<Condition> conditions, LicensedProduct product)
}

private Emission emitFor(Condition condition, LicensedProduct product) {
boolean satisfied = false;
try {
satisfied = expressionIsSatisfied(condition);
expressionIsSatisfied(condition);
} catch (ExpressionParsingException e) {
new Emission.Failed(new BaseEmissionFailureDiagnostic()); // FIXME: ytbd: explain
return new Emission.Failed(new BaseFailureDiagnostic(//
new LicenseInvalid(), //
String.format(ConditionsEvaluationMessages.getString("BasePermissionEmittingService.parse_failed"), // //$NON-NLS-1$
condition.evaluationInstructions().expression()), //
e));
} catch (ExpressionEvaluationException e) {
return new Emission.Failed(new BaseFailureDiagnostic(//
new LicenseDoesNotMatch(), //
String.format(
ConditionsEvaluationMessages.getString("BasePermissionEmittingService.evaluation_failed"), // //$NON-NLS-1$
condition.evaluationInstructions().expression()), //
e));
} catch (LicensingException e) {
new Emission.Failed(new BaseEmissionFailureDiagnostic()); // FIXME: ytbd: explain
}
if (satisfied) {
return new Emission.Successful(Arrays.asList(//
new BasePermission(//
product, //
condition, //
ZonedDateTime.now(), //
expiration(condition.validityPeriod()))));
} else {
return new Emission.Failed(new BaseEmissionFailureDiagnostic()); // FIXME: ytbd: explain
return new Emission.Failed(new BaseFailureDiagnostic(//
new LicenseCheckFailed(), //
String.format(ConditionsEvaluationMessages.getString("BasePermissionEmittingService.failed"), // //$NON-NLS-1$
condition.evaluationInstructions().expression()), //
e));
}
return new Emission.Successful(Arrays.asList(//
new BasePermission(//
product, //
condition, //
ZonedDateTime.now(), //
expiration(condition.validityPeriod()))));
}

private boolean expressionIsSatisfied(Condition condition) throws LicensingException, ExpressionParsingException {
private void expressionIsSatisfied(Condition condition)
throws ExpressionParsingException, ExpressionEvaluationException, LicensingException {
ExpressionTokenAssessmentService assessor = //
evaluator(condition.evaluationInstructions().type());
ParsedExpression expression = new FormalizedExpression( //
condition.evaluationInstructions().expression(), //
parsers.get()).get();
ExpressionEvaluationService evaluator = evaluators.get().service(expression.protocol());
return evaluator.evaluate(expression, assessor);
evaluator.evaluate(expression, assessor);
}

private ExpressionTokenAssessmentService evaluator(EvaluationType type) throws LicensingException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import java.util.Objects;

import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionParsingException;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionPasringService;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionParsingService;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionProtocol;
import org.eclipse.passage.lic.internal.api.conditions.evaluation.ParsedExpression;
import org.eclipse.passage.lic.internal.api.registry.Registry;
Expand All @@ -37,9 +37,9 @@ final class FormalizedExpression {

private final String raw;
private final String separator;
private final Registry<ExpressionProtocol, ExpressionPasringService> parsers;
private final Registry<ExpressionProtocol, ExpressionParsingService> parsers;

FormalizedExpression(String raw, String separator, Registry<ExpressionProtocol, ExpressionPasringService> parsers) {
FormalizedExpression(String raw, String separator, Registry<ExpressionProtocol, ExpressionParsingService> parsers) {
Objects.requireNonNull(raw, "RetrievedExpression::raw"); //$NON-NLS-1$
Objects.requireNonNull(separator, "RetrievedExpression::separator"); //$NON-NLS-1$
Objects.requireNonNull(parsers, "RetrievedExpression::parsers"); //$NON-NLS-1$
Expand All @@ -48,15 +48,15 @@ final class FormalizedExpression {
this.parsers = parsers;
}

FormalizedExpression(String raw, Registry<ExpressionProtocol, ExpressionPasringService> parsers) {
FormalizedExpression(String raw, Registry<ExpressionProtocol, ExpressionParsingService> parsers) {
this(raw, "!!", parsers); //$NON-NLS-1$
}

ParsedExpression get() throws ExpressionParsingException {
return service().parsed(content());
}

private ExpressionPasringService service() {
private ExpressionParsingService service() {
ExpressionProtocol protocol = protocol();
return parsers.hasService(protocol) //
? parsers.service(protocol) //
Expand Down
Loading