1 /* 2 * Copyright (C) 2007 ETH Zurich 3 * 4 * This file is part of Fosstrak (www.fosstrak.org). 5 * 6 * Fosstrak is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License version 2.1, as published by the Free Software Foundation. 9 * 10 * Fosstrak is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with Fosstrak; if not, write to the Free 17 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301 USA 19 */ 20 21 package org.fosstrak.capturingapp; 22 23 import java.util.ArrayList; 24 import java.util.Collection; 25 import java.util.LinkedList; 26 27 import org.apache.log4j.Logger; 28 import org.drools.KnowledgeBase; 29 import org.drools.KnowledgeBaseFactory; 30 import org.drools.builder.KnowledgeBuilder; 31 import org.drools.builder.KnowledgeBuilderFactory; 32 import org.drools.definition.KnowledgePackage; 33 import org.drools.runtime.StatefulKnowledgeSession; 34 import org.drools.runtime.StatelessKnowledgeSession; 35 import org.fosstrak.ale.xsd.ale.epcglobal.ECReports; 36 import org.fosstrak.epcis.model.EPCISDocumentType; 37 38 39 /** 40 * An ECReportHandler receives a ECReport and generates a EPCIS document that 41 * can be sent to an EPCIS repository. If you would like to implement your own 42 * handler, you will find some guide-lines below. In addition you can get inspiration 43 * by the default reports handler {@link DefaultECReportHandler}. 44 * <h3>Default Execution Sequence:</h3> 45 * If you do not override the method <code>LinkedList<EPCISDocumentType> 46 * handle(ECReports reports)</code>, the execution sequence of the handler is 47 * as following: 48 * <ol> 49 * <li><code>abstract loadRules()</code>: load drools rules from disc.</li> 50 * <li><code>checkErrors()</code>: checks if errors in the rule-set. If there are 51 * errors, an exception is thrown.</li> 52 * <li><code>registerKnowledgeBase()</code>: Create a new knowledge-base 53 * <code>kbase</code> from the <code>kbuilder</code>.</li> 54 * <li><code>createSession()</code>: Creates a drools session. By default, a 55 * <strong>StatefulKnowledgeSession</strong> is created. Notice, that all 56 * subsequent generic methods are programmed in such a way, that they can 57 * handle both <strong>StatefulKnowledgeSession</strong> and 58 * <strong>StatelessKnowledgeSession</strong>.</li> 59 * <li><code>prepareGlobalCollector(ksession)</code>: Registers a collector for 60 * the EPCIS documents. If you override the method <code>handle</code> make 61 * sure, to call this method <strong>before</strong> you execute the drools 62 * rules as otherwise third-party drools rules might crash.</li> 63 * <li><code>executeSession(reports):</code>: Execute the drools rules. In 64 * <strong>StatefulKnowledgeSession</strong> the ECReports are injected via 65 * <code>insert</code>, in <strong>StatelessKnowledgeSession</strong> the 66 * rules are executed via <code>execute</code>.</li> 67 * <li><code>collectResults()</code>: Retrieves the results from the global 68 * collector and submits them to the capture application for further delivery.</li> 69 * </ol> 70 * <h3>Methods to be implemented:</h3> 71 * <ul> 72 * <li><code>loadRules()</code>. It is required, that 73 * this method loads the drools rules from the file-system into 74 * <code>kbuilder</code>.<br/> 75 * Example:<br/> 76 * <code>kbuilder.add(</code><br/> 77 * <code> ResourceFactory.newClassPathResource(</code><br/> 78 * <code> changeSet,</code><br/> 79 * <code> DefaultECReportHandler.class),</code><br/> 80 * <code> ResourceType.CHANGE_SET);</code> 81 * </li> 82 * @author sawielan 83 * 84 */ 85 public abstract class ECReportsHandler { 86 87 /** the name of the global variable for the drools results. */ 88 public static final String RESULTS = "epcisResults"; 89 90 /** array that will collect the results from the drools executions. */ 91 protected ArrayList<Object> epcis = null; 92 93 /** the knowledge builder. */ 94 protected final KnowledgeBuilder kbuilder = 95 KnowledgeBuilderFactory.newKnowledgeBuilder(); 96 97 /** the knowledge base. */ 98 protected KnowledgeBase kbase = null; 99 100 /** 101 * the knowledge session. this is object to allow either state-full or 102 * state-less sessions. 103 */ 104 protected Object ksession; 105 106 // logger 107 private static final Logger log = Logger.getLogger(ECReportsHandler.class); 108 109 110 /** the default change-set to load with the drools rules. */ 111 public static final String DEFAULT_RULE_SET = "changeset.xml"; 112 113 // the change-set to load with the drools rules. 114 protected final String changeSet; 115 116 /** 117 * default constructor. sets the rule-set to 'changeset.xml'. 118 */ 119 public ECReportsHandler() { 120 changeSet = DEFAULT_RULE_SET; 121 } 122 123 /** 124 * create a new handler with a non default change set. 125 * @param changeSet 126 */ 127 public ECReportsHandler(String changeSet) { 128 this.changeSet = changeSet; 129 } 130 131 /** 132 * @return the change set that is used with this handler. 133 */ 134 public final String getChangeSet() { 135 return changeSet; 136 } 137 138 /** 139 * this method is invoked from the capture application whenever a new 140 * ECReports is received. The resulting EPCIS document will be delivered 141 * automatically to the EPCIS repository. If you set the return value 142 * to <code>null</code>, then no report is generated. <br/> 143 * <strong>NOTICE:</strong> If you override this method, make sure that you 144 * invoke the method <code>prepareGlobalCollector(ksession)</code> 145 * <strong>before</strong> you invoke the rule-set. 146 * @param reports the ECReports with the EPC data. 147 * @return an EPCIS document when this handler consumed the report, null 148 * otherwise. 149 * @throws RuntimeException when the rule(s) could not be compiled. 150 */ 151 public LinkedList<EPCISDocumentType> handle(ECReports reports) throws RuntimeException { 152 log.debug("handling report."); 153 loadRules(); 154 checkErrors(); 155 registerKnowledgeBase(); 156 createSession(); 157 158 // THIS ONE IS IMPORTANT FOR THE DEFAULT COLLECT RESULTS!!! 159 prepareGlobalCollector(ksession); 160 161 executeSession(reports); 162 163 return collectResults(); 164 } 165 166 /** 167 * executes the drools session. The method is aware of the two different 168 * knowledge-sessions <code>StatelessKnowledgeSession</code> and 169 * <code>StatefulKnowledgeSession</code> and invokes them respectively. 170 * @param reports the ECReports. 171 */ 172 public void executeSession(ECReports reports) { 173 log.debug("executing session."); 174 if (ksession instanceof StatelessKnowledgeSession) { 175 ((StatelessKnowledgeSession)ksession).execute(reports); 176 } else if (ksession instanceof StatefulKnowledgeSession) { 177 StatefulKnowledgeSession sks = (StatefulKnowledgeSession) ksession; 178 sks.insert(reports); 179 sks.fireAllRules(); 180 } 181 } 182 183 /** 184 * create a new knowledge session. By default a 185 * <code>StatefulKnowledgeSession</code> is generated. 186 */ 187 protected void createSession() { 188 log.debug("create new stateless knowledge session."); 189 ksession = kbase.newStatefulKnowledgeSession(); 190 } 191 192 /** 193 * register the knowledge base from the rule builder in the knowledge base. 194 */ 195 protected void registerKnowledgeBase() { 196 if (null != kbase) { 197 // we don't need to register the knowledge-base again. 198 return; 199 } 200 log.debug("register knowledge base."); 201 // get the compiled packages 202 final Collection<KnowledgePackage> pkgs = kbuilder 203 .getKnowledgePackages(); 204 205 // add the packages to a knowledge-base (deploy the knowledge packages). 206 kbase = KnowledgeBaseFactory.newKnowledgeBase(); 207 kbase.addKnowledgePackages(pkgs); 208 } 209 210 /** 211 * check the knowledge-builder for errors in the rules. 212 * @throws RuntimeException when there are errors in the rules. 213 */ 214 protected void checkErrors() throws RuntimeException { 215 // Check the builder for errors 216 if (kbuilder.hasErrors()) { 217 log.debug(kbuilder.getErrors().toString()); 218 throw new RuntimeException(kbuilder.getErrors().toString()); 219 } 220 } 221 222 /** 223 * prepare a global collector where the rules can store the results to. 224 * @param ksession the session holding the collector. 225 */ 226 protected void prepareGlobalCollector(Object ksession) { 227 log.debug("preparing global collector."); 228 epcis = new ArrayList<Object> (); 229 if (ksession instanceof StatefulKnowledgeSession) { 230 ((StatefulKnowledgeSession) ksession).setGlobal(RESULTS, epcis); 231 } else if (ksession instanceof StatelessKnowledgeSession) { 232 ((StatelessKnowledgeSession)ksession).setGlobal(RESULTS, epcis); 233 } 234 } 235 236 /** 237 * collect the results from the rules evaluation and put them into a nice 238 * typed linked list. 239 * @return a linked list holding the result set. 240 */ 241 protected LinkedList<EPCISDocumentType> collectResults() { 242 log.debug("collecting results."); 243 LinkedList<EPCISDocumentType> results 244 = new LinkedList<EPCISDocumentType> (); 245 for (Object o : epcis) { 246 if ((null != o) && (o instanceof EPCISDocumentType)) { 247 results.add((EPCISDocumentType)o); 248 } else if (null != o) { 249 log.debug(o); 250 } 251 } 252 return results; 253 } 254 255 /** 256 * <code>// parse and compile the rule from file</code><br/> 257 * <code> kbuilder.add(ResourceFactory.newClassPathResource(</code><br/> 258 * <code> "HelloWorld.drl", HelloWorld.class), ResourceType.DRL);</code><br/> 259 */ 260 public abstract void loadRules(); 261 262 /** 263 * in case of a <code>StatefullKnowledgeSession</code> we have to call 264 * dispose at the end of execution. 265 */ 266 public void dispose() { 267 if (ksession instanceof StatefulKnowledgeSession) { 268 ((StatefulKnowledgeSession) ksession).dispose(); 269 } 270 } 271 }