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.camel;
018    
019    
020    import javax.jbi.messaging.ExchangeStatus;
021    import javax.jbi.messaging.Fault;
022    import javax.jbi.messaging.InOnly;
023    import javax.jbi.messaging.MessageExchange;
024    import javax.jbi.messaging.MessagingException;
025    import javax.jbi.messaging.RobustInOnly;
026    import javax.xml.namespace.QName;
027    import javax.xml.transform.Source;
028    
029    import org.apache.camel.Endpoint;
030    import org.apache.camel.Exchange;
031    import org.apache.camel.Processor;
032    import org.apache.servicemix.JbiConstants;
033    import org.apache.servicemix.common.ServiceUnit;
034    import org.apache.servicemix.common.endpoints.ProviderEndpoint;
035    import org.apache.servicemix.jbi.FaultException;
036    
037    /**
038     * A JBI endpoint which when invoked will delegate to a Camel endpoint
039     *
040     * @version $Revision: 426415 $
041     */
042    public class CamelProviderEndpoint extends ProviderEndpoint {
043    
044        public static final QName SERVICE_NAME = new QName("http://activemq.apache.org/camel/schema/jbi", "provider");
045    
046        private JbiBinding binding;
047    
048        private Processor camelProcessor;
049    
050        public CamelProviderEndpoint(ServiceUnit serviceUnit, QName service, String endpoint, JbiBinding binding, Processor camelProcessor) {
051            super(serviceUnit, service, endpoint);
052            this.camelProcessor = camelProcessor;
053            this.binding = binding;
054        }
055    
056        public CamelProviderEndpoint(ServiceUnit serviceUnit, Endpoint camelEndpoint, JbiBinding binding, Processor camelProcessor) {
057            this(serviceUnit, SERVICE_NAME, camelEndpoint.getEndpointUri(), binding, camelProcessor);
058        }
059    
060        @Override
061        public void process(MessageExchange exchange) throws Exception {
062            // The component acts as a provider, this means that another component has requested our service
063            // As this exchange is active, this is either an in or a fault (out are sent by this component)
064    
065            if (exchange.getRole() == MessageExchange.Role.PROVIDER) {
066                // Exchange is finished
067                if (exchange.getStatus() == ExchangeStatus.DONE) {
068                    return;
069                // Exchange has been aborted with an exception
070                } else if (exchange.getStatus() == ExchangeStatus.ERROR) {
071                    return;
072                // Exchange is active
073                } else {
074                    handleActiveProviderExchange(exchange);
075    
076                }
077            // Unsupported role: this should never happen has we never create exchanges
078            } else {
079                throw new IllegalStateException("Unsupported role: " + exchange.getRole());
080            }
081        }
082    
083    
084        protected void handleActiveProviderExchange(MessageExchange exchange) throws Exception {
085            // Fault message
086            if (exchange.getFault() != null) {
087                done(exchange);
088            // In message
089            } else if (exchange.getMessage("in") != null) {
090                if (logger.isDebugEnabled()) {
091                    logger.debug("Received exchange: " + exchange);
092                }
093                Exchange camelExchange = binding.createExchange(exchange);
094                camelProcessor.process(camelExchange);
095                if (camelExchange.isFailed()) {
096                    handleFailure(exchange, camelExchange);
097                } else {
098                    handleSuccess(exchange, camelExchange);
099                }
100            // This is not compliant with the default MEPs
101            } else {
102                throw new IllegalStateException("Provider exchange is ACTIVE, but no in or fault is provided");
103            }
104        }
105    
106        /*
107         * Handle successful camel invocation
108         */
109        private void handleSuccess(MessageExchange exchange, Exchange camelExchange) throws MessagingException {
110            binding.copyFromCamelToJbi(camelExchange, exchange);
111            if (exchange instanceof InOnly || exchange instanceof RobustInOnly) {
112                done(exchange);
113            } else {
114                doSend(exchange);
115            }
116        }
117    
118        /*
119         * Handle failure during camel route invocation 
120         */
121        private void handleFailure(MessageExchange exchange, Exchange camelExchange) throws MessagingException {
122            if (camelExchange.getFault(false) == null || camelExchange.getFault(false).getBody() == null) {
123                fail(exchange, binding.extractException(camelExchange));
124            } else {
125                Fault fault = exchange.createFault();
126                fault.setContent(camelExchange.getFault().getBody(Source.class));
127                if (isFaultCapable(exchange)) {
128                    exchange.setFault(fault);
129                    doSend(exchange);
130                } else {
131                    // MessageExchange is not capable of conveying faults -- sending the information as an error instead
132                    fail(exchange, new FaultException("Fault occured for " + exchange.getPattern() + " exchange", exchange, fault));
133                }
134            } 
135        }
136    
137        /*
138         * Check if the exchange is capable of conveying fault messages
139         */
140        private boolean isFaultCapable(MessageExchange exchange) {
141            return !(exchange instanceof InOnly);
142        }
143        
144        /*
145         * Send back the response, taking care to use sendSync when necessary
146         */
147        private void doSend(MessageExchange exchange) throws MessagingException {
148            boolean txSync = exchange.isTransacted() && Boolean.TRUE.equals(exchange.getProperty(JbiConstants.SEND_SYNC));
149            if (txSync && ExchangeStatus.ACTIVE.equals(exchange.getStatus())) {
150                sendSync(exchange);
151            } else {
152                send(exchange);
153            }        
154        }
155    }