Skip to content

Commit

Permalink
Implemented asynchronous decision engine. Resolved #210
Browse files Browse the repository at this point in the history
  • Loading branch information
stlhrt committed Jul 18, 2019
1 parent febfe78 commit 3f517bf
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2018-present PowerFlows.org - all rights reserved.
*
* 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 org.powerflows.dmn.engine;


import org.powerflows.dmn.engine.model.decision.Decision;
import org.powerflows.dmn.engine.model.evaluation.result.DecisionResult;
import org.powerflows.dmn.engine.model.evaluation.variable.DecisionVariables;

import java.util.concurrent.CompletableFuture;

/**
* AsynchronousdDecision engine contract.
*/
public interface AsyncDecisionEngine {

/**
* @param decision Definition of decision
* @param decisionVariables Variables used in evaluation
* @return a promise of evaluation result
*/
CompletableFuture<DecisionResult> evaluate(Decision decision, DecisionVariables decisionVariables);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2018-present PowerFlows.org - all rights reserved.
*
* 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 org.powerflows.dmn.engine;

import org.powerflows.dmn.engine.model.decision.Decision;
import org.powerflows.dmn.engine.model.evaluation.result.DecisionResult;
import org.powerflows.dmn.engine.model.evaluation.variable.DecisionVariables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;

/**
* Default asynchronous decision engine implementation.
*/
public class DefaultAsyncDecisionEngine implements AsyncDecisionEngine {
private static final Logger LOG = LoggerFactory.getLogger(DefaultAsyncDecisionEngine.class);
private final DecisionEngine decisionEngine;
private final ExecutorService executorService;

public DefaultAsyncDecisionEngine(DecisionEngine decisionEngine, ExecutorService executorService) {
this.decisionEngine = decisionEngine;
this.executorService = executorService;
}

@Override
public CompletableFuture<DecisionResult> evaluate(final Decision decision, final DecisionVariables decisionVariables) {
final CompletableFuture<DecisionResult> result = new CompletableFuture<>();
executorService.submit(() -> {
try {
result.complete(decisionEngine.evaluate(decision, decisionVariables));
} catch (Exception e) {
LOG.debug("Error completing decision evaluation", e);
result.completeExceptionally(e);
}
});

return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2018-present PowerFlows.org - all rights reserved.
*
* 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 org.powerflows.dmn.engine.configuration;


import org.powerflows.dmn.engine.AsyncDecisionEngine;

/**
* Interface for AsyncDecisionEngine configurers.
*
* @see DefaultAsyncDecisionEngineConfiguration
*/
public interface AsyncDecisionEngineConfiguration {

/**
* Create ready to use decision engine.
*
* @return AsyncDecisionEngine instance
*/
AsyncDecisionEngine configure();

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@


import org.powerflows.dmn.engine.DecisionEngine;
import org.powerflows.dmn.engine.evaluator.expression.provider.binding.MethodBinding;

import java.util.List;

/**
* Interface for DecisionEngine configurers.
*
* @see DefaultDecisionEngineConfiguration
*/
public interface DecisionEngineConfiguration {
public interface DecisionEngineConfiguration<T extends DecisionEngineConfiguration> {

/**
* Create ready to use decision engine.
Expand All @@ -32,4 +36,5 @@ public interface DecisionEngineConfiguration {
*/
DecisionEngine configure();

T methodBindings(List<MethodBinding> methodBindings);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2018-present PowerFlows.org - all rights reserved.
*
* 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 org.powerflows.dmn.engine.configuration;

import lombok.Setter;
import lombok.experimental.Accessors;
import org.powerflows.dmn.engine.AsyncDecisionEngine;
import org.powerflows.dmn.engine.DefaultAsyncDecisionEngine;
import org.powerflows.dmn.engine.evaluator.expression.provider.binding.MethodBinding;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* Default DecisionEngine configurer.
* Builds and configures {@link DefaultAsyncDecisionEngine} instances.
* Allows for customisation of method binding configuration.
* Uses single thread asynchronous executor service by default @see Executors.newSingleThreadExecutor
*
* @see MethodBinding
*/
@Accessors(chain = true, fluent = true)
public class DefaultAsyncDecisionEngineConfiguration implements AsyncDecisionEngineConfiguration {
@Setter
private DecisionEngineConfiguration engineConfiguration;
@Setter
private ExecutorService executorService;
@Setter
private List<MethodBinding> methodBindings = Collections.emptyList();

@Override
public AsyncDecisionEngine configure() {
initEngineConfiguration();
initMethodBindings();
initExecutorService();

return new DefaultAsyncDecisionEngine(engineConfiguration.configure(), executorService);
}

private void initMethodBindings() {
engineConfiguration.methodBindings(methodBindings);
}

private void initEngineConfiguration() {
if (engineConfiguration == null) {
engineConfiguration = new DefaultDecisionEngineConfiguration();
}
}

private void initExecutorService() {
if (executorService == null) {
executorService = Executors.newSingleThreadExecutor();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
* @see MethodBinding
*/
@Accessors(chain = true, fluent = true)
public class DefaultDecisionEngineConfiguration implements DecisionEngineConfiguration {
public class DefaultDecisionEngineConfiguration implements DecisionEngineConfiguration<DefaultDecisionEngineConfiguration> {

@Setter
private List<MethodBinding> methodBindings = Collections.emptyList();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2018-present PowerFlows.org - all rights reserved.
*
* 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 org.powerflows.dmn.engine.configuration

import org.powerflows.dmn.engine.AsyncDecisionEngine
import org.powerflows.dmn.engine.DecisionEngine
import org.powerflows.dmn.engine.model.decision.Decision
import org.powerflows.dmn.engine.model.evaluation.result.DecisionResult
import org.powerflows.dmn.engine.model.evaluation.variable.DecisionVariables
import spock.lang.Shared
import spock.lang.Specification

import java.util.concurrent.CompletableFuture

class DefaultAsyncDecisionEngineConfigurationSpec extends Specification {

@Shared
private AsyncDecisionEngineConfiguration asyncDecisionEngineConfiguration

@Shared
private DecisionEngineConfiguration decisionEngineConfiguration

@Shared
private DecisionEngine decisionEngine

@Shared
private DecisionResult decisionResult

@Shared
private Decision decision

void setupSpec() {
decisionResult = Mock()
decisionEngine = Mock() {
evaluate(_, _) >> decisionResult
}
decisionEngineConfiguration = Mock() {
configure() >>
decisionEngine

}
asyncDecisionEngineConfiguration = new DefaultAsyncDecisionEngineConfiguration(
engineConfiguration: decisionEngineConfiguration
)

decision = Mock()
}

void 'should evaluate asynchronously'() {
given:
final DecisionVariables decisionVariables = new DecisionVariables([:])

when:
final AsyncDecisionEngine asyncDecisionEngine = asyncDecisionEngineConfiguration.configure()

then:
asyncDecisionEngine != null

when:
final CompletableFuture<DecisionResult> future = asyncDecisionEngine.evaluate(decision, decisionVariables)

then:
future != null
DecisionResult result = future.get()
result != null

0 * _
}
}

0 comments on commit 3f517bf

Please sign in to comment.