View Javadoc

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&lt;EPCISDocumentType&gt;
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>&nbsp;&nbsp;ResourceFactory.newClassPathResource(</code><br/>
78   * <code>&nbsp;&nbsp;&nbsp;&nbsp;changeSet,</code><br/>
79   * <code>&nbsp;&nbsp;&nbsp;&nbsp;DefaultECReportHandler.class),</code><br/>
80   * <code>&nbsp;&nbsp;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 }