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    }