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 }