/* * Copyright 2011 JBoss Inc * * 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.drools.core.fluent.impl; import org.drools.core.command.ConversationContextManager; import org.drools.core.command.RequestContextImpl; import org.drools.core.command.impl.ExecutableCommand; import org.drools.core.runtime.InternalLocalRunner; import org.kie.api.time.SessionPseudoClock; import org.drools.core.world.impl.ContextManagerImpl; import org.kie.api.command.Command; import org.kie.api.runtime.Context; import org.kie.api.runtime.Executable; import org.kie.api.runtime.KieSession; import org.kie.api.runtime.RequestContext; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.PriorityQueue; import java.util.Set; import java.util.concurrent.TimeUnit; public class PseudoClockRunner implements InternalLocalRunner { private final Map<String, Context> appContexts = new HashMap<>(); private ConversationContextManager cvnManager = new ConversationContextManager(); private long counter; private Set<KieSession> ksessions = new HashSet<>(); private PriorityQueue<Batch> queue = new PriorityQueue<>( BatchSorter.instance); private final long startTime; public PseudoClockRunner() { this(System.currentTimeMillis()); } public PseudoClockRunner( long startTime ) { this.startTime = startTime; } @Override public RequestContext execute( Executable executable, RequestContext ctx ) { executeBatches( ( (InternalExecutable) executable ), ctx ); executeQueue( ctx ); return ctx; } private void executeBatches( InternalExecutable executable, RequestContext ctx ) { for (Batch batch : executable.getBatches()) { if ( batch.getDistance() == 0L ) { executeBatch( batch, ctx ); } else { queue.add( batch ); } } } private void executeQueue( RequestContext ctx ) { while ( !queue.isEmpty() ) { Batch batch = queue.remove(); long timeNow = startTime + batch.getDistance(); for ( KieSession ksession : ksessions ) { updateKieSessionTime(timeNow, batch.getDistance(), ksession); // make sure all sessions are set to timeNow } for (Command cmd : batch.getCommands() ) { Object returned = ((ExecutableCommand)cmd).execute( ctx ); if ( returned != null ) { ctx.setResult( returned ); if ( returned instanceof KieSession ) { KieSession ksession = ( KieSession ) returned; updateKieSessionTime(timeNow, batch.getDistance(), ksession); // make sure all sessions are set to timeNow ksessions.add((KieSession)returned); } } } } } private void executeBatch( Batch batch, RequestContext ctx ) { // anything with a temporal distance of 0 is executed now // everything else must be handled by a priority queue and timer afterwards. for (Command cmd : batch.getCommands() ) { Object returned = ((ExecutableCommand)cmd).execute( ctx ); if ( returned != null ) { ctx.setResult( returned ); if ( returned instanceof KieSession ) { KieSession ksession = ( KieSession ) returned; updateKieSessionTime( startTime, 0, ksession ); // make sure all sessions are set to timeNow ksessions.add((KieSession)returned); } } } } private void updateKieSessionTime(long timeNow, long distance, KieSession ksession) { SessionPseudoClock clock = ksession.getSessionClock(); if ( clock.getCurrentTime() != timeNow ) { long newTime = startTime + distance; long currentTime = clock.getCurrentTime(); clock.advanceTime( newTime - currentTime, TimeUnit.MILLISECONDS ); } } private static class BatchSorter implements Comparator<Batch> { public static BatchSorter instance = new BatchSorter(); @Override public int compare(Batch o1, Batch o2) { if(o1.getDistance() > o2.getDistance()) { return 1; } else if(o1.getDistance() < o2.getDistance()) { return -1; } return 0; } } public RequestContext createContext() { return new RequestContextImpl( counter++, new ContextManagerImpl( appContexts ), cvnManager ); } }