All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.drools.core.rule.SlidingTimeWindow Maven / Gradle / Ivy

There is a newer version: 9.44.0.Final
Show newest version
/*
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
 *
 * 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.rule;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Collection;
import java.util.PriorityQueue;

import org.drools.core.common.EventFactHandle;
import org.drools.core.common.InternalFactHandle;
import org.drools.core.common.InternalWorkingMemory;
import org.drools.core.common.WorkingMemoryAction;
import org.drools.core.marshalling.impl.MarshallerReaderContext;
import org.drools.core.marshalling.impl.MarshallerWriteContext;
import org.drools.core.marshalling.impl.PersisterEnums;
import org.drools.core.marshalling.impl.ProtobufMessages;
import org.drools.core.marshalling.impl.ProtobufMessages.ActionQueue.Action;
import org.drools.core.marshalling.impl.ProtobufMessages.Timers.Timer;
import org.drools.core.marshalling.impl.TimersInputMarshaller;
import org.drools.core.marshalling.impl.TimersOutputMarshaller;
import org.drools.core.phreak.PropagationEntry;
import org.drools.core.reteoo.ObjectTypeNode;
import org.drools.core.reteoo.WindowNode;
import org.drools.core.reteoo.WindowNode.WindowMemory;
import org.drools.core.spi.PropagationContext;
import org.drools.core.time.Job;
import org.drools.core.time.JobContext;
import org.drools.core.time.JobHandle;
import org.drools.core.time.TimerService;
import org.drools.core.time.impl.PointInTimeTrigger;

import static org.drools.core.common.PhreakPropagationContextFactory.createPropagationContextForFact;

public class SlidingTimeWindow
        implements
        Externalizable,
        Behavior {

    protected long size;
    // stateless job
    private static final BehaviorJob job = new BehaviorJob();

    protected int nodeId;

    public SlidingTimeWindow() {
        this( 0 );
    }

    public SlidingTimeWindow(final long size) {
        super();
        this.size = size;
    }

    /**
     * @inheritDoc
     *
     * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
     */
    public void readExternal(final ObjectInput in) throws IOException,
                                                          ClassNotFoundException {
        this.size = in.readLong();
        this.nodeId = in.readInt();
    }

    /**
     * @inheritDoc
     *
     * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
     */
    public void writeExternal(final ObjectOutput out) throws IOException {
        out.writeLong( this.size );
        out.writeInt( this.nodeId );
    }

    public BehaviorType getType() {
        return BehaviorType.TIME_WINDOW;
    }

    public void setWindowNode(WindowNode windowNode) {
        this.nodeId = windowNode.getId();
    }

    /**
     * @return the size
     */
    public long getSize() {
        return size;
    }

    /**
     * @param size the size to set
     */
    public void setSize(final long size) {
        this.size = size;
    }

    public Behavior.Context createContext() {
        return new SlidingTimeWindowContext();
    }

    public boolean assertFact(final Object context,
                              final InternalFactHandle fact,
                              final PropagationContext pctx,
                              final InternalWorkingMemory workingMemory) {
        final SlidingTimeWindowContext queue = (SlidingTimeWindowContext) context;
        final EventFactHandle handle = (EventFactHandle) fact;
        long currentTime = workingMemory.getTimerService().getCurrentTime();
        if ( isExpired( currentTime, handle ) ) {
            return false;
        }

        queue.add( handle );
        if ( queue.peek() == handle ) {
            // update next expiration time
            updateNextExpiration( handle,
                                  workingMemory,
                                  queue,
                                  nodeId );
        }

        return true;
    }

    public void retractFact(final Object context,
                            final InternalFactHandle fact,
                            final PropagationContext pctx,
                            final InternalWorkingMemory workingMemory) {
        final SlidingTimeWindowContext queue = (SlidingTimeWindowContext) context;
        final EventFactHandle handle = (EventFactHandle) fact;
        // it may be a call back to expire the tuple that is already being expired
        if ( queue.getExpiringHandle() != handle ) {
            if ( queue.peek() == handle ) {
                // it was the head of the queue
                queue.poll();
                // update next expiration time
                updateNextExpiration( queue.peek(),
                                      workingMemory,
                                      queue,
                                      nodeId);
            } else {
                queue.remove( handle );
            }
        }
    }

    public void expireFacts(final Object context,
                            final PropagationContext pctx,
                            final InternalWorkingMemory workingMemory) {
        TimerService clock = workingMemory.getTimerService();
        long currentTime = clock.getCurrentTime();
        SlidingTimeWindowContext queue = (SlidingTimeWindowContext) context;

        EventFactHandle handle = queue.peek();
        while ( handle != null && isExpired( currentTime,
                                             handle ) ) {
            queue.setExpiringHandle( handle );
            queue.remove();
            if( handle.isValid()) {
                // if not expired yet, expire it
                final PropagationContext expiresPctx = createPropagationContextForFact( workingMemory, handle, PropagationContext.Type.EXPIRATION );
                ObjectTypeNode.doRetractObject(handle, expiresPctx, workingMemory);
            }
            queue.setExpiringHandle( null );
            handle = queue.peek();
        }
        // update next expiration time
        updateNextExpiration( handle,
                              workingMemory,
                              queue,
                              nodeId );
    }

    protected boolean isExpired(final long currentTime,
                                final EventFactHandle handle) {
        return handle.getStartTimestamp() + this.size <= currentTime;
    }

    protected void updateNextExpiration(final InternalFactHandle fact,
                                        final InternalWorkingMemory workingMemory,
                                        final Behavior.Context context,
                                        final int nodeId) {
        TimerService clock = workingMemory.getTimerService();
        if ( fact != null ) {
            long nextTimestamp = ((EventFactHandle) fact).getStartTimestamp() + getSize();
            if ( nextTimestamp < clock.getCurrentTime() ) {
                // Past and out-of-order events should not be insert,
                // but the engine silently accepts them anyway, resulting in possibly undesirable behaviors
                workingMemory.queueWorkingMemoryAction(new BehaviorExpireWMAction(nodeId, this, context));
            } else {
                JobContext jobctx = new BehaviorJobContext( nodeId, workingMemory, this, context);
                JobHandle handle = clock.scheduleJob( job,
                                                      jobctx,
                                                      new PointInTimeTrigger( nextTimestamp, null, null ) );
                jobctx.setJobHandle( handle );
            }
        }
    }

    public long getExpirationOffset() {
        return this.size;
    }

    public String toString() {
        return "SlidingTimeWindow( size=" + size + " )";
    }

    public static class SlidingTimeWindowContext
            implements
            Behavior.Context,
            Externalizable {

        private PriorityQueue queue;
        private EventFactHandle                expiringHandle;

        public SlidingTimeWindowContext() {
            this.queue = new PriorityQueue( 16 ); // arbitrary size... can we improve it?
        }

        @SuppressWarnings("unchecked")
        public void readExternal(ObjectInput in) throws IOException,
                                                        ClassNotFoundException {
            this.queue = (PriorityQueue) in.readObject();
            this.expiringHandle = (EventFactHandle) in.readObject();
        }

        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeObject( this.queue );
            out.writeObject( this.expiringHandle );
        }

        public EventFactHandle getExpiringHandle() {
            return expiringHandle;
        }

        public void setExpiringHandle( EventFactHandle expiringHandle ) {
            this.expiringHandle = expiringHandle;
        }

        public void setExpiringTuple(EventFactHandle expiringHandle) {
            this.expiringHandle = expiringHandle;
        }

        public void add(EventFactHandle handle) {
            queue.add( handle );
        }

        public void remove(EventFactHandle handle) {
            queue.remove( handle );
        }

        public EventFactHandle peek() {
            return queue.peek( );
        }

        public EventFactHandle poll() {
            return queue.poll( );
        }

        public EventFactHandle remove() {
            return queue.remove( );
        }

        public Collection getFactHandles() {
            return queue;
        }
    }

    public static class BehaviorJobContextTimerOutputMarshaller implements TimersOutputMarshaller {
        public void write(JobContext jobCtx,
                          MarshallerWriteContext outputCtx) throws IOException {
            outputCtx.writeShort( PersisterEnums.BEHAVIOR_TIMER );
            // BehaviorJob, no state            
            BehaviorJobContext bjobCtx = ( BehaviorJobContext ) jobCtx;

            // write out SlidingTimeWindowContext
            SlidingTimeWindowContext slCtx = ( SlidingTimeWindowContext ) bjobCtx.behaviorContext;

            EventFactHandle handle = slCtx.peek();
            outputCtx.writeInt( handle.getId() );

//            BetaNode node = (BetaNode) handle.getTupleSink();
//            outputCtx.writeInt( node.getId() );
//
//            Behavior[] behaviors = node.getBehaviors();
//            int i = 0;
//            for ( ; i < behaviors.length; i++ ) {
//                if ( behaviors[i] == bjobCtx.behavior ) {
//                    break;
//                }
//            }
//            outputCtx.writeInt( i );
        }

        public Timer serialize(JobContext jobCtx,
                               MarshallerWriteContext outputCtx) {
            // BehaviorJob, no state            
            BehaviorJobContext bjobCtx = ( BehaviorJobContext ) jobCtx;
            // write out SlidingTimeWindowContext
            SlidingTimeWindowContext slCtx = ( SlidingTimeWindowContext ) bjobCtx.behaviorContext;

            EventFactHandle handle = slCtx.peek();

            return ProtobufMessages.Timers.Timer.newBuilder()
                                                .setType( ProtobufMessages.Timers.TimerType.BEHAVIOR )
                                                .setBehavior( ProtobufMessages.Timers.BehaviorTimer.newBuilder()
                                                                                                   .setHandleId( handle.getId() )
                                                                                                   .build() )
                                                .build();
        }
    }

    public static class BehaviorJobContextTimerInputMarshaller implements TimersInputMarshaller {
        public void read(MarshallerReaderContext inCtx) throws IOException, ClassNotFoundException {
            int sinkId = inCtx.readInt();
            WindowNode windowNode = (WindowNode) inCtx.sinks.get( sinkId );

            WindowMemory memory = inCtx.wm.getNodeMemory( windowNode );

            Object[] behaviorContext = ( Object[]  ) memory.behaviorContext;

            int i = inCtx.readInt();
        }

        public void deserialize(MarshallerReaderContext inCtx,
                                Timer _timer) throws ClassNotFoundException {
            int i = _timer.getBehavior().getHandleId();
        }
    }


    public static class BehaviorJobContext
            implements
            JobContext,
            Externalizable {
        public InternalWorkingMemory workingMemory;
        public int                   nodeId;
        public Behavior              behavior;
        public Behavior.Context      behaviorContext;
        public JobHandle             handle;

        public BehaviorJobContext(int                   nodeId,
                                  InternalWorkingMemory workingMemory,
                                  Behavior behavior,
                                  Behavior.Context behaviorContext) {
            super();
            this.nodeId = nodeId;
            this.workingMemory = workingMemory;
            this.behavior = behavior;
            this.behaviorContext = behaviorContext;
        }

        public JobHandle getJobHandle() {
            return this.handle;
        }

        public void setJobHandle(JobHandle jobHandle) {
            this.handle = jobHandle;
        }

        public void readExternal(ObjectInput in) throws IOException,
                                                        ClassNotFoundException {
            //this.behavior = (O)
        }

        public void writeExternal(ObjectOutput out) throws IOException {
            // TODO Auto-generated method stub
        }

        @Override
        public InternalWorkingMemory getWorkingMemory() {
            return workingMemory;
        }
    }

    public static class BehaviorJob
            implements
            Job {

        public void execute(JobContext ctx) {
            BehaviorJobContext context = (BehaviorJobContext) ctx;
            context.workingMemory.queueWorkingMemoryAction( new BehaviorExpireWMAction( context.nodeId,
                                                                                        context.behavior,
                                                                                        context.behaviorContext ) );
        }

    }

    public static class BehaviorExpireWMAction
            extends PropagationEntry.AbstractPropagationEntry
            implements WorkingMemoryAction {
        private final Behavior behavior;
        private final Behavior.Context context;
        private final int nodeId;

        public BehaviorExpireWMAction(final int nodeId,
                                      Behavior behavior,
                                      Behavior.Context context) {
            super();
            this.nodeId = nodeId;
            this.behavior = behavior;
            this.context = context;
        }

        public BehaviorExpireWMAction(MarshallerReaderContext inCtx) throws IOException {
            nodeId = inCtx.readInt();
            WindowNode windowNode = (WindowNode) inCtx.sinks.get( nodeId );

            WindowMemory memory = inCtx.wm.getNodeMemory( windowNode );

            Behavior.Context[] behaviorContext = memory.behaviorContext;

            int i = inCtx.readInt();

            this.behavior = windowNode.getBehaviors()[i];
            this.context = behaviorContext[i];
        }

        public BehaviorExpireWMAction(MarshallerReaderContext context,
                                      Action _action) {
            nodeId =_action.getBehaviorExpire().getNodeId();
            WindowNode windowNode = (WindowNode) context.sinks.get( nodeId );

            WindowMemory memory = context.wm.getNodeMemory( windowNode );

            Behavior.Context[] behaviorContext = memory.behaviorContext;

            int i = 0; //  <==== this needs fixing

            this.behavior = windowNode.getBehaviors()[i];
            this.context = behaviorContext[i];
        }

        public void execute(InternalWorkingMemory workingMemory) {
            this.behavior.expireFacts( context,
                                       null,
                                       workingMemory );
        }

        public ProtobufMessages.ActionQueue.Action serialize(MarshallerWriteContext outputCtx) {
            ProtobufMessages.ActionQueue.BehaviorExpire _be = ProtobufMessages.ActionQueue.BehaviorExpire.newBuilder()
                                                                                                         .setNodeId( nodeId )
                                                                                                         .build();

            return ProtobufMessages.ActionQueue.Action.newBuilder()
                                                      .setType( ProtobufMessages.ActionQueue.ActionType.BEHAVIOR_EXPIRE )
                                                      .setBehaviorExpire( _be )
                                                      .build();

        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy