/* * 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.impl; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EventListener; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import org.drools.core.SessionConfiguration; import org.drools.core.base.MapGlobalResolver; import org.drools.core.command.impl.ContextImpl; import org.drools.core.command.impl.ExecutableCommand; import org.drools.core.command.impl.RegistryContext; import org.drools.core.command.runtime.BatchExecutionCommandImpl; import org.drools.core.command.runtime.rule.FireAllRulesCommand; import org.drools.core.common.InternalFactHandle; import org.drools.core.common.WorkingMemoryFactory; import org.drools.core.management.DroolsManagementAgent; import org.drools.core.runtime.impl.ExecutionResultImpl; import org.kie.api.KieBase; import org.kie.api.command.BatchExecutionCommand; import org.kie.api.command.Command; import org.kie.api.event.process.ProcessEventListener; import org.kie.api.event.rule.AgendaEventListener; import org.kie.api.event.rule.RuleRuntimeEventListener; import org.kie.api.runtime.Channel; import org.kie.api.runtime.Environment; import org.kie.api.runtime.Globals; import org.kie.api.runtime.KieSession; import org.kie.api.runtime.KieSessionConfiguration; import org.kie.api.runtime.ObjectFilter; import org.kie.api.runtime.StatelessKieSession; import org.kie.api.runtime.rule.FactHandle; import org.kie.internal.runtime.StatefulKnowledgeSession; import org.kie.internal.runtime.StatelessKnowledgeSession; public class StatelessKnowledgeSessionImpl extends AbstractRuntime implements StatelessKnowledgeSession, StatelessKieSession { private InternalKnowledgeBase kBase; private MapGlobalResolver sessionGlobals = new MapGlobalResolver(); private Map<String, Channel> channels = new HashMap<String, Channel>(); private final List<ListnerHolder> listeners = new CopyOnWriteArrayList<ListnerHolder>(); private KieSessionConfiguration conf; private Environment environment; private WorkingMemoryFactory wmFactory; private AtomicBoolean mbeanRegistered = new AtomicBoolean(false); private DroolsManagementAgent.CBSKey mbeanRegisteredCBSKey; private AtomicLong wmCreated = new AtomicLong(0); public StatelessKnowledgeSessionImpl() { } public StatelessKnowledgeSessionImpl(final InternalKnowledgeBase kBase, final KieSessionConfiguration conf) { this.kBase = kBase; this.conf = (conf != null) ? conf : kBase.getSessionConfiguration(); this.environment = EnvironmentFactory.newEnvironment(); this.wmFactory = kBase.getConfiguration().getComponentFactory().getWorkingMemoryFactory(); } public InternalKnowledgeBase getKnowledgeBase() { return this.kBase; } public StatefulKnowledgeSession newWorkingMemory() { this.kBase.readLock(); try { StatefulKnowledgeSession ksession = (StatefulKnowledgeSession) wmFactory.createWorkingMemory(this.kBase.nextWorkingMemoryCounter(), this.kBase, (SessionConfiguration) this.conf, this.environment); StatefulKnowledgeSessionImpl ksessionImpl = (StatefulKnowledgeSessionImpl) ksession; ((Globals) ksessionImpl.getGlobalResolver()).setDelegate(this.sessionGlobals); registerListeners( ksessionImpl ); for( Map.Entry<String, Channel> entry : this.channels.entrySet() ) { ksession.registerChannel( entry.getKey(), entry.getValue() ); } wmCreated.incrementAndGet(); return ksession; } finally { this.kBase.readUnlock(); } } public void initMBeans(String containerId, String kbaseId, String ksessionName) { if (kBase.getConfiguration() != null && kBase.getConfiguration().isMBeansEnabled() && mbeanRegistered.compareAndSet(false, true)) { this.mbeanRegisteredCBSKey = new DroolsManagementAgent.CBSKey(containerId, kbaseId, ksessionName); DroolsManagementAgent.getInstance().registerKnowledgeSessionUnderName(mbeanRegisteredCBSKey, this); } } public long getWorkingMemoryCreatec() { return wmCreated.get(); } private void registerListeners( StatefulKnowledgeSessionImpl wm ) { if ( listeners.isEmpty()) { return; } for (ListnerHolder listnerHolder : listeners ) { switch (listnerHolder.type) { case AGENDA: wm.addEventListener( (AgendaEventListener)listnerHolder.listener ); break; case RUNTIME: wm.addEventListener( (RuleRuntimeEventListener)listnerHolder.listener ); break; case PROCESS: wm.addEventListener( (ProcessEventListener)listnerHolder.listener ); break; } } } public void addEventListener(AgendaEventListener listener) { listeners.add( new ListnerHolder( ListnerHolder.Type.AGENDA, listener ) ); } public Collection<AgendaEventListener> getAgendaEventListeners() { return (Collection<AgendaEventListener>) getListeners(ListnerHolder.Type.AGENDA); } public void removeEventListener(AgendaEventListener listener) { listeners.remove( new ListnerHolder( ListnerHolder.Type.AGENDA, listener ) ); } public void addEventListener(RuleRuntimeEventListener listener) { listeners.add( new ListnerHolder( ListnerHolder.Type.RUNTIME, listener ) ); } public void removeEventListener(RuleRuntimeEventListener listener) { listeners.remove( new ListnerHolder( ListnerHolder.Type.RUNTIME, listener ) ); } public Collection<RuleRuntimeEventListener> getRuleRuntimeEventListeners() { return (Collection<RuleRuntimeEventListener>) getListeners(ListnerHolder.Type.RUNTIME); } public void addEventListener(ProcessEventListener listener) { listeners.add( new ListnerHolder( ListnerHolder.Type.PROCESS, listener ) ); } public Collection<ProcessEventListener> getProcessEventListeners() { return (Collection<ProcessEventListener>) getListeners(ListnerHolder.Type.PROCESS); } private Collection<? extends EventListener> getListeners(ListnerHolder.Type type) { if ( listeners.isEmpty()) { return Collections.emptySet(); } Collection<EventListener> l = new ArrayList<EventListener>(); for (ListnerHolder listnerHolder : listeners ) { if (listnerHolder.type == type) { l.add( listnerHolder.listener ); } } return l; } public void removeEventListener(ProcessEventListener listener) { listeners.remove( new ListnerHolder( ListnerHolder.Type.RUNTIME, listener ) ); } public void setGlobal(String identifier, Object value) { this.sessionGlobals.setGlobal(identifier, value); } public Globals getGlobals() { return this.sessionGlobals; } @Override public void registerChannel(String name, Channel channel) { this.channels.put(name, channel); } @Override public void unregisterChannel(String name) { this.channels.remove(name); } @Override public Map<String, Channel> getChannels() { return Collections.unmodifiableMap( this.channels ); } @Override public KieBase getKieBase() { return getKnowledgeBase(); } public <T> T execute(Command<T> command) { StatefulKnowledgeSession ksession = newWorkingMemory(); RegistryContext context = new ContextImpl().register( KieSession.class, ksession ); try { if ( command instanceof BatchExecutionCommand ) { ((RegistryContext) context).register( ExecutionResultImpl.class, new ExecutionResultImpl() ); } ((StatefulKnowledgeSessionImpl) ksession).startBatchExecution(); Object o = ((ExecutableCommand) command).execute( context ); // did the user take control of fireAllRules, if not we will auto execute boolean autoFireAllRules = true; if ( command instanceof FireAllRulesCommand ) { autoFireAllRules = false; } else if ( command instanceof BatchExecutionCommandImpl ) { for ( Command nestedCmd : ((BatchExecutionCommandImpl) command).getCommands() ) { if ( nestedCmd instanceof FireAllRulesCommand ) { autoFireAllRules = false; break; } } } if ( autoFireAllRules ) { ksession.fireAllRules(); } if ( command instanceof BatchExecutionCommand ) { return (T) ((RegistryContext) context).lookup( ExecutionResultImpl.class ); } else { return (T) o; } } finally { ((StatefulKnowledgeSessionImpl) ksession).endBatchExecution(); dispose(ksession); } } public void execute(Object object) { StatefulKnowledgeSession ksession = newWorkingMemory(); try { ksession.insert( object ); ksession.fireAllRules(); } finally { dispose(ksession); } } public void execute(Iterable objects) { StatefulKnowledgeSession ksession = newWorkingMemory(); try { for ( Object object : objects ) { ksession.insert( object ); } ksession.fireAllRules(); } finally { dispose(ksession); } } public List executeWithResults(Iterable objects, ObjectFilter filter) { List list = new ArrayList(); StatefulKnowledgeSession ksession = newWorkingMemory(); try { for ( Object object : objects ) { ksession.insert( object ); } ksession.fireAllRules(); for (FactHandle fh : ksession.getFactHandles(filter)) { list.add(((InternalFactHandle) fh).getObject()); } } finally { dispose(ksession); } return list; } public Environment getEnvironment() { return environment; } protected void dispose(StatefulKnowledgeSession ksession) { ksession.dispose(); } private static class ListnerHolder { enum Type { AGENDA, RUNTIME, PROCESS } final Type type; final EventListener listener; private ListnerHolder( Type type, EventListener listener ) { this.type = type; this.listener = listener; } @Override public boolean equals( Object obj ) { if ( this == obj ) { return true; } if ( obj == null || !(obj instanceof ListnerHolder) ) { return false; } ListnerHolder that = (ListnerHolder) obj; return type == that.type && listener == that.listener; } @Override public int hashCode() { int result = type.hashCode(); result = 31 * result + listener.hashCode(); return result; } } }