/* * 2012-3 Red Hat Inc. and/or its affiliates and other contributors. * * 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.overlord.rtgov.ep.drools; import java.text.MessageFormat; import java.util.logging.Level; import java.util.logging.Logger; import com.fasterxml.jackson.annotation.JsonIgnore; import org.kie.api.KieBase; import org.kie.api.KieBaseConfiguration; import org.kie.api.KieServices; import org.kie.api.builder.KieBuilder; import org.kie.api.builder.KieFileSystem; import org.kie.api.builder.Message; import org.kie.api.builder.ReleaseId; import org.kie.api.builder.Results; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieSession; import org.kie.api.runtime.KieSessionConfiguration; import org.kie.api.runtime.conf.ClockTypeOption; import org.kie.api.runtime.rule.EntryPoint; import org.overlord.rtgov.ep.EventProcessor; import org.overlord.rtgov.ep.ResultHandler; import org.overlord.rtgov.internal.ep.DefaultEPContext; /** * This class represents the Drools implementation of the Event * Processor. * */ public class DroolsEventProcessor extends EventProcessor { /** * Event processing mode: stream. */ public static final String EVENT_PROCESSING_MODE_STREAM = "stream"; /** * Event processing mode: cloud (default). */ public static final String EVENT_PROCESSING_MODE_CLOUD = "cloud"; private static final String DROOLS_EVENT_PROCESSING_MODE = "drools.eventProcessingMode"; private static final Logger LOG=Logger.getLogger(DroolsEventProcessor.class.getName()); private DefaultEPContext _context=null; private KieSession _session=null; private String _ruleName=null; private String _eventProcessingMode=null; private String _clockType=null; private boolean _streamMode=false; private Thread _streamThread=null; private static java.util.concurrent.atomic.AtomicInteger _count=new java.util.concurrent.atomic.AtomicInteger(); /** * {@inheritDoc} */ public void init() throws Exception { _streamMode = (_eventProcessingMode != null && _eventProcessingMode.equalsIgnoreCase(EVENT_PROCESSING_MODE_STREAM)); if (_streamMode && !getAsynchronous()) { throw new IllegalArgumentException("DroolsEventProcessor for ruleName '"+_ruleName +"' must be configured as 'asynchronous' when using 'stream' eventProcessingMode"); } else if (!_streamMode && getAsynchronous()) { throw new IllegalArgumentException("DroolsEventProcessor for ruleName '"+_ruleName +"' must NOT be configured as 'asynchronous' when using 'cloud' eventProcessingMode"); } _context = new DefaultEPContext(getServices(), getParameters()); _session = createSession(); if (LOG.isLoggable(Level.FINEST)) { LOG.finest("DroolsEventProcessor init: ruleName="+_ruleName+" session="+_session +" streamMode="+_streamMode); } } /** * {@inheritDoc} */ @JsonIgnore public void setResultHandler(ResultHandler handler) { super.setResultHandler(handler); _context.setResultHandler(handler); } /** * This method returns the rule name. * * @return The rule name */ public String getRuleName() { return (_ruleName); } /** * This method sets the rule name. * * @param ruleName The rule name */ public void setRuleName(String ruleName) { _ruleName = ruleName; } /** * This method returns the event processing mode (default * is cloud mode). * * @return The event processing mode */ public String getEventProcessingMode() { return (_eventProcessingMode); } /** * This method sets the event processing mode (default * is cloud mode). * * @param eventProcessingMode The event processing mode */ public void setEventProcessingMode(String eventProcessingMode) { _eventProcessingMode = eventProcessingMode; } /** * This method determines whether stream processing is being used. * * @return Whether stream processing mode is being used */ protected boolean isStreamEventProcessingMode() { return (_streamMode); } /** * This method returns the clock type (default * is realtime). * * @return The clock type */ public String getClockType() { return (_clockType); } /** * This method sets the clock type (default * is realtime). * * @param clockType The clock type */ public void setClockType(String clockType) { _clockType = clockType; } /** * {@inheritDoc} */ public java.io.Serializable process(String source, java.io.Serializable event, int retriesLeft) throws Exception { java.io.Serializable ret=null; synchronized (this) { if (LOG.isLoggable(Level.FINEST)) { LOG.finest("Process event '"+event+" from source '"+source +"' on Drools Event Processor '"+getRuleName() +"'"); } if (!getAsynchronous()) { _context.handle(null); } // Get entry point // TODO: If not simple lookup, then may want to cache this EntryPoint entryPoint=_session.getEntryPoint(source); if (entryPoint != null) { if (LOG.isLoggable(Level.FINEST)) { LOG.finest("Insert event '"+event+" from source '"+source +"' on Drools Event Processor '"+getRuleName() +"' into entry point "+entryPoint); } entryPoint.insert(event); // TODO: Not sure if possible to delay evaluation, until after // all events in batch have been processed/inserted - but then // how to trace the individual results?? if (!getAsynchronous()) { _session.fireAllRules(); } } else { String mesg=MessageFormat.format( java.util.PropertyResourceBundle.getBundle( "ep-drools.Messages").getString("EP-DROOLS-3"), source, getRuleName()); LOG.severe(mesg); throw new Exception(mesg); } if (!getAsynchronous()) { ret = (java.io.Serializable)_context.getResult(); } if (ret instanceof Exception) { throw (Exception)ret; } } return ret; } /** * This method creates a stateful knowledge session per * Drools event processor. * * @return The stateful knowledge session * @throws Exception Failed to create session */ private KieSession createSession() throws Exception { KieBase kbase = loadRuleBase(); if (kbase != null) { KieSessionConfiguration config = KieServices.Factory.get().newKieSessionConfiguration(); if (getClockType() != null) { config.setOption(ClockTypeOption.get(getClockType())); } final KieSession ret = kbase.newKieSession(config, null); if (ret != null) { ret.setGlobal("epc", _context); if (isStreamEventProcessingMode()) { _streamThread = new Thread(new Runnable() { public void run() { if (LOG.isLoggable(Level.FINE)) { LOG.fine("Starting stream session thread for rule: "+getRuleName()); } ret.fireUntilHalt(); } }); _streamThread.start(); try { synchronized (this) { wait(2000); } } catch (Exception e) { LOG.log(Level.SEVERE, "Failed to wait for session thread to start", e); } } else { ret.fireAllRules(); } } else { String mesg=MessageFormat.format( java.util.PropertyResourceBundle.getBundle( "ep-drools.Messages").getString("EP-DROOLS-2"), getRuleName()); LOG.severe(mesg); throw new Exception(mesg); } return (ret); } return (null); } /** * This method loads the rule base associated with the Drools * event processor. * * @return The knowledge base * @throws Exception Failed to load rule base */ private KieBase loadRuleBase() throws Exception { String droolsRuleBase=getRuleName()+".drl"; try { KieServices kieServices = KieServices.Factory.get(); ReleaseId releaseId = KieServices.Factory.get().newReleaseId("org.overlord.rtgov.tmp", getRuleName(), String.valueOf(_count.getAndIncrement())); KieFileSystem kieFileSystem = kieServices.newKieFileSystem().generateAndWritePomXML(releaseId); kieFileSystem.write(kieServices.getResources().newClassPathResource(droolsRuleBase)); KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem).buildAll(); Results results = kieBuilder.getResults(); if (results.hasMessages(Message.Level.ERROR)) { StringBuffer buf=new StringBuffer(); for (Message message : results.getMessages(Message.Level.ERROR)) { buf.append("ERROR: "+message.toString().trim()+"\r\n"); } throw new Exception(buf.toString()); } KieContainer kieContainer = kieServices.newKieContainer(releaseId); if (isStreamEventProcessingMode()) { java.util.Properties properties=new java.util.Properties(); properties.setProperty(DROOLS_EVENT_PROCESSING_MODE, EVENT_PROCESSING_MODE_STREAM); KieBaseConfiguration kieBaseConfiguration = KieServices.Factory.get().newKieBaseConfiguration(properties); return (kieContainer.newKieBase(kieBaseConfiguration)); } return kieContainer.getKieBase(); } catch (Throwable e) { String mesg=MessageFormat.format( java.util.PropertyResourceBundle.getBundle( "ep-drools.Messages").getString("EP-DROOLS-1"), droolsRuleBase, getRuleName()); LOG.log(Level.SEVERE, mesg, e); throw new Exception(mesg, e); } } /** * {@inheritDoc} */ public void close() throws Exception { if (LOG.isLoggable(Level.FINEST)) { LOG.finest("DroolsEventProcessor close: ruleName="+_ruleName+" session="+_session +" streamMode="+_streamMode); } if (_session != null) { _session.dispose(); } } }