001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.servicemix.drools;
018
019 import java.io.InputStream;
020 import java.io.InputStreamReader;
021 import java.net.URL;
022 import java.util.List;
023 import java.util.Map;
024 import java.util.concurrent.ConcurrentHashMap;
025 import java.util.concurrent.ConcurrentMap;
026
027 import javax.jbi.JBIException;
028 import javax.jbi.management.DeploymentException;
029 import javax.jbi.messaging.ExchangeStatus;
030 import javax.jbi.messaging.MessageExchange;
031 import javax.jbi.messaging.MessageExchange.Role;
032 import javax.jbi.messaging.MessagingException;
033
034
035 import javax.jbi.servicedesc.ServiceEndpoint;
036 import javax.xml.namespace.NamespaceContext;
037 import javax.xml.namespace.QName;
038
039 import org.apache.servicemix.JbiConstants;
040 import org.apache.servicemix.common.DefaultComponent;
041 import org.apache.servicemix.common.ServiceUnit;
042 import org.apache.servicemix.common.endpoints.ProviderEndpoint;
043 import org.apache.servicemix.drools.model.JbiHelper;
044 import org.apache.servicemix.jbi.util.MessageUtil;
045 import org.drools.RuleBase;
046 import org.drools.WorkingMemory;
047 import org.drools.compiler.RuleBaseLoader;
048 import org.springframework.core.io.Resource;
049
050 /**
051 *
052 * @author gnodet
053 * @org.apache.xbean.XBean element="endpoint"
054 */
055 public class DroolsEndpoint extends ProviderEndpoint {
056
057 private RuleBase ruleBase;
058 private Resource ruleBaseResource;
059 private URL ruleBaseURL;
060 private NamespaceContext namespaceContext;
061 private QName defaultTargetService;
062 private String defaultTargetURI;
063 private Map<String, Object> globals;
064 private List<Object> assertedObjects;
065 private ConcurrentMap<String, JbiHelper> pending = new ConcurrentHashMap<String, JbiHelper>();
066
067 public DroolsEndpoint() {
068 super();
069 }
070
071 public DroolsEndpoint(DefaultComponent component, ServiceEndpoint endpoint) {
072 super(component, endpoint);
073 }
074
075 public DroolsEndpoint(ServiceUnit su, QName service, String endpoint) {
076 super(su, service, endpoint);
077 }
078
079 /**
080 * @return the ruleBase
081 */
082 public RuleBase getRuleBase() {
083 return ruleBase;
084 }
085
086 /**
087 * @param ruleBase the ruleBase to set
088 */
089 public void setRuleBase(RuleBase ruleBase) {
090 this.ruleBase = ruleBase;
091 }
092
093 /**
094 * @return the ruleBaseResource
095 */
096 public Resource getRuleBaseResource() {
097 return ruleBaseResource;
098 }
099
100 /**
101 * @param ruleBaseResource the ruleBaseResource to set
102 */
103 public void setRuleBaseResource(Resource ruleBaseResource) {
104 this.ruleBaseResource = ruleBaseResource;
105 }
106
107 /**
108 * @return the ruleBaseURL
109 */
110 public URL getRuleBaseURL() {
111 return ruleBaseURL;
112 }
113
114 /**
115 * @param ruleBaseURL the ruleBaseURL to set
116 */
117 public void setRuleBaseURL(URL ruleBaseURL) {
118 this.ruleBaseURL = ruleBaseURL;
119 }
120
121 /**
122 * @return the namespaceContext
123 */
124 public NamespaceContext getNamespaceContext() {
125 return namespaceContext;
126 }
127
128 /**
129 * @param namespaceContext the namespaceContext to set
130 */
131 public void setNamespaceContext(NamespaceContext namespaceContext) {
132 this.namespaceContext = namespaceContext;
133 }
134
135 /**
136 * @return the variables
137 */
138 public Map<String, Object> getGlobals() {
139 return globals;
140 }
141
142 /**
143 * @param variables the variables to set
144 */
145 public void setGlobals(Map<String, Object> variables) {
146 this.globals = variables;
147 }
148
149 public void validate() throws DeploymentException {
150 super.validate();
151 if (ruleBase == null && ruleBaseResource == null && ruleBaseURL == null) {
152 throw new DeploymentException("Property ruleBase, ruleBaseResource or ruleBaseURL must be set");
153 }
154 }
155
156 public void start() throws Exception {
157 super.start();
158 if (ruleBase == null) {
159 InputStream is = null;
160 try {
161 if (ruleBaseResource != null) {
162 is = ruleBaseResource.getInputStream();
163 } else if (ruleBaseURL != null) {
164 is = ruleBaseURL.openStream();
165 } else {
166 throw new IllegalArgumentException("Property ruleBase, ruleBaseResource "
167 + "or ruleBaseURL must be set");
168 }
169 RuleBaseLoader loader = RuleBaseLoader.getInstance();
170 ruleBase = loader.loadFromReader(new InputStreamReader(is));
171 } catch (Exception e) {
172 throw new JBIException(e);
173 } finally {
174 if (is != null) {
175 is.close();
176 }
177 }
178 }
179 }
180
181 /* (non-Javadoc)
182 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#process(
183 * javax.jbi.messaging.MessageExchange, javax.jbi.messaging.NormalizedMessage)
184 */
185 public void process(MessageExchange exchange) throws Exception {
186 // drools(exchange);
187 if (exchange.getRole() == Role.PROVIDER) {
188 handleProviderExchange(exchange);
189 } else {
190 handleConsumerExchange(exchange);
191 }
192
193 }
194 /*
195 * Handle a consumer exchange
196 */
197 private void handleConsumerExchange(MessageExchange exchange)
198 throws MessagingException {
199 String correlation = (String) exchange.getProperty(DroolsComponent.DROOLS_CORRELATION_ID);
200 JbiHelper helper = pending.get(correlation);
201 if (helper != null) {
202 MessageExchange original = helper.getExchange()
203 .getInternalExchange();
204 if (exchange.getStatus() == ExchangeStatus.DONE) {
205 done(original);
206 } else if (exchange.getStatus() == ExchangeStatus.ERROR) {
207 fail(original, exchange.getError());
208 } else {
209 if (exchange.getFault() != null) {
210 MessageUtil.transferFaultToFault(exchange, original);
211 } else {
212 MessageUtil.transferOutToOut(exchange, original);
213 }
214 send(original);
215 }
216 // update the rule engine's working memory to trigger post-done
217 // rules
218 helper.update();
219 } else {
220 logger.debug("No pending exchange found for "
221 + correlation
222 + ", no additional rules will be triggered");
223 }
224 }
225
226 //protected void postProcess(MessageExchange exchange, WorkingMemory memory) throws Exception {
227 private void handleProviderExchange(MessageExchange exchange) throws Exception {
228 if (exchange.getStatus() == ExchangeStatus.ACTIVE) {
229 drools(exchange);
230 }
231 }
232
233 public static String getCorrelationId(MessageExchange exchange) {
234 Object correlation = exchange.getProperty(JbiConstants.CORRELATION_ID);
235 if (correlation == null) {
236 return exchange.getExchangeId();
237 } else {
238 return correlation.toString();
239 }
240 }
241
242 protected void drools(MessageExchange exchange) throws Exception {
243 WorkingMemory memory = createWorkingMemory(exchange);
244 JbiHelper helper = populateWorkingMemory(memory, exchange);
245 pending.put(exchange.getExchangeId(), helper);
246 memory.fireAllRules();
247
248 if (helper.getRulesFired() < 1) {
249 fail(exchange, new Exception("No rules have handled the exchange. Check your rule base."));
250 } else {
251 //a rule was triggered and the message has been answered or faulted by the drools endpoint
252 if (helper.isExchangeHandled()) {
253 pending.remove(exchange);
254 }
255
256 }
257 }
258
259 protected WorkingMemory createWorkingMemory(MessageExchange exchange) throws Exception {
260 return ruleBase.newWorkingMemory();
261 }
262
263 protected JbiHelper populateWorkingMemory(WorkingMemory memory, MessageExchange exchange) throws Exception {
264 JbiHelper helper = new JbiHelper(this, exchange, memory);
265 memory.setGlobal("jbi", helper);
266 if (assertedObjects != null) {
267 for (Object o : assertedObjects) {
268 memory.assertObject(o);
269 }
270 }
271 if (globals != null) {
272 for (Map.Entry<String, Object> e : globals.entrySet()) {
273 memory.setGlobal(e.getKey(), e.getValue());
274 }
275 }
276 return helper;
277 }
278
279 public QName getDefaultTargetService() {
280 return defaultTargetService;
281 }
282
283 public void setDefaultTargetService(QName defaultTargetService) {
284 this.defaultTargetService = defaultTargetService;
285 }
286
287 public String getDefaultTargetURI() {
288 return defaultTargetURI;
289 }
290
291 public void setDefaultTargetURI(String defaultTargetURI) {
292 this.defaultTargetURI = defaultTargetURI;
293 }
294
295 public List<Object> getAssertedObjects() {
296 return assertedObjects;
297 }
298
299 public void setAssertedObjects(List<Object> assertedObjects) {
300 this.assertedObjects = assertedObjects;
301 }
302
303 public String getDefaultRouteURI() {
304 if (defaultTargetURI != null) {
305 return defaultTargetURI;
306 } else if (defaultTargetService != null) {
307 String nsURI = defaultTargetService.getNamespaceURI();
308 String sep = (nsURI.indexOf("/") > 0) ? "/" : ":";
309 return "service:" + nsURI + sep
310 + defaultTargetService.getLocalPart();
311 } else {
312 return null;
313 }
314 }
315
316 @Override
317 protected void send(MessageExchange me) throws MessagingException {
318 // must be a DONE/ERROR so removing any pending contexts
319 pending.remove(me.getExchangeId());
320 super.send(me);
321 }
322
323 }