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.client;
018    
019    import java.util.Iterator;
020    import java.util.Map;
021    
022    import javax.jbi.JBIException;
023    import javax.jbi.component.ComponentContext;
024    import javax.jbi.messaging.DeliveryChannel;
025    import javax.jbi.messaging.ExchangeStatus;
026    import javax.jbi.messaging.Fault;
027    import javax.jbi.messaging.InOnly;
028    import javax.jbi.messaging.InOptionalOut;
029    import javax.jbi.messaging.InOut;
030    import javax.jbi.messaging.MessageExchange;
031    import javax.jbi.messaging.MessageExchangeFactory;
032    import javax.jbi.messaging.MessagingException;
033    import javax.jbi.messaging.NormalizedMessage;
034    import javax.jbi.messaging.RobustInOnly;
035    import javax.jbi.servicedesc.ServiceEndpoint;
036    import javax.xml.namespace.QName;
037    
038    import org.w3c.dom.DocumentFragment;
039    
040    import org.apache.servicemix.jbi.FaultException;
041    import org.apache.servicemix.jbi.NoOutMessageAvailableException;
042    import org.apache.servicemix.jbi.api.EndpointResolver;
043    import org.apache.servicemix.jbi.api.Message;
044    import org.apache.servicemix.jbi.container.ActivationSpec;
045    import org.apache.servicemix.jbi.container.JBIContainer;
046    import org.apache.servicemix.jbi.messaging.DefaultMarshaler;
047    import org.apache.servicemix.jbi.messaging.PojoMarshaler;
048    import org.apache.servicemix.jbi.resolver.EndpointFilter;
049    import org.apache.servicemix.jbi.resolver.ExternalInterfaceNameEndpointResolver;
050    import org.apache.servicemix.jbi.resolver.ExternalServiceNameEndpointResolver;
051    import org.apache.servicemix.jbi.resolver.InterfaceNameEndpointResolver;
052    import org.apache.servicemix.jbi.resolver.NullEndpointFilter;
053    import org.apache.servicemix.jbi.resolver.ServiceAndEndpointNameResolver;
054    import org.apache.servicemix.jbi.resolver.ServiceNameEndpointResolver;
055    import org.apache.servicemix.jbi.resolver.URIResolver;
056    
057    /**
058     * A Facade around the {@link ComponentContext} to provide the {@link ServiceMixClient} API which is useful for
059     * working with JBI from inside a POJO based JBI Component which doesn't derive from {@link PojoSupport}
060     *
061     * @version $Revision: 690190 $
062     */
063    public class ServiceMixClientFacade implements ServiceMixClient {
064    
065        private ComponentContext context;
066        private EndpointFilter filter = NullEndpointFilter.getInstance();
067        private PojoMarshaler marshaler = new DefaultMarshaler();
068        private MessageExchangeFactory exchangeFactory;
069    
070        public ServiceMixClientFacade(ComponentContext context) {
071            this.context = context;
072        }
073    
074        /**
075         * Provides the JBI container used for message dispatch.
076         */
077        public ServiceMixClientFacade(JBIContainer container) throws JBIException {
078            this(container, new ActivationSpec());
079        }
080    
081        /**
082         * Provides the JBI container and the activation specification, which can be used to register this
083         * client at a specific endpoint so that default container routing rules can be configured via dependency injection
084         * and the client endpoint metadata can be configured to allow services to talk to this client.
085         */
086        public ServiceMixClientFacade(JBIContainer container, ActivationSpec activationSpec) throws JBIException {
087            activationSpec.setComponent(this);
088            container.activateComponent(activationSpec);
089        }
090    
091        public InOnly createInOnlyExchange() throws MessagingException {
092            InOnly exchange = getExchangeFactory().createInOnlyExchange();
093            NormalizedMessage in = exchange.createMessage();
094            exchange.setInMessage(in);
095            return exchange;
096        }
097    
098        public InOnly createInOnlyExchange(EndpointResolver resolver) throws JBIException {
099            InOnly exchange = createInOnlyExchange();
100            configureEndpoint(exchange, resolver);
101            return exchange;
102        }
103    
104        public InOut createInOutExchange() throws MessagingException {
105            InOut exchange = getExchangeFactory().createInOutExchange();
106            NormalizedMessage in = exchange.createMessage();
107            exchange.setInMessage(in);
108            return exchange;
109        }
110    
111        public InOut createInOutExchange(EndpointResolver resolver) throws JBIException {
112            InOut exchange = createInOutExchange();
113            configureEndpoint(exchange, resolver);
114            return exchange;
115        }
116    
117        public InOptionalOut createInOptionalOutExchange() throws MessagingException {
118            InOptionalOut exchange = getExchangeFactory().createInOptionalOutExchange();
119            NormalizedMessage in = exchange.createMessage();
120            exchange.setInMessage(in);
121            return exchange;
122        }
123    
124        public InOptionalOut createInOptionalOutExchange(EndpointResolver resolver) throws JBIException {
125            InOptionalOut exchange = createInOptionalOutExchange();
126            configureEndpoint(exchange, resolver);
127            return exchange;
128        }
129    
130        public RobustInOnly createRobustInOnlyExchange() throws MessagingException {
131            RobustInOnly exchange = getExchangeFactory().createRobustInOnlyExchange();
132            NormalizedMessage in = exchange.createMessage();
133            exchange.setInMessage(in);
134            return exchange;
135        }
136    
137        public RobustInOnly createRobustInOnlyExchange(EndpointResolver resolver) throws JBIException {
138            RobustInOnly exchange = getExchangeFactory().createRobustInOnlyExchange();
139            configureEndpoint(exchange, resolver);
140            return exchange;
141        }
142    
143        public Destination createDestination(String uri) throws MessagingException {
144            return new DefaultDestination(this, uri);
145        }
146    
147        public void send(MessageExchange exchange) throws MessagingException {
148            getDeliveryChannel().send(exchange);
149        }
150            
151        public void send(Message message) throws MessagingException {
152            send(message.getExchange());
153        }
154    
155        public boolean sendSync(MessageExchange exchange) throws MessagingException {
156            return getDeliveryChannel().sendSync(exchange);
157        }
158    
159        public boolean sendSync(MessageExchange exchange, long timeout) throws MessagingException {
160            return getDeliveryChannel().sendSync(exchange, timeout);
161        }
162    
163        public MessageExchange receive() throws MessagingException {
164            return getDeliveryChannel().accept();
165        }
166    
167        public MessageExchange receive(long timeout) throws MessagingException {
168            return getDeliveryChannel().accept(timeout);
169        }
170    
171        public ComponentContext getContext() {
172            return context;
173        }
174    
175        public DeliveryChannel getDeliveryChannel() throws MessagingException {
176            return getContext().getDeliveryChannel();
177        }
178    
179        /**
180         * Provide access to the default message exchange exchangeFactory, lazily creating one.
181         */
182        public MessageExchangeFactory getExchangeFactory() throws MessagingException {
183            if (exchangeFactory == null && context != null) {
184                exchangeFactory = getDeliveryChannel().createExchangeFactory();
185            }
186            return exchangeFactory;
187        }
188    
189        /**
190         * A helper method to indicate that the message exchange is complete
191         * which will set the status to {@link ExchangeStatus#DONE} and send the message
192         * on the delivery channel.
193         *
194         * @param exchange
195         * @throws MessagingException
196         */
197        public void done(MessageExchange exchange) throws MessagingException {
198            exchange.setStatus(ExchangeStatus.DONE);
199            getDeliveryChannel().send(exchange);
200        }
201    
202        /**
203         * A helper method which fails and completes the given exchange with the specified fault
204         */
205        public void fail(MessageExchange exchange, Fault fault) throws MessagingException {
206            exchange.setFault(fault);
207            getDeliveryChannel().send(exchange);
208        }
209    
210        /**
211         * A helper method which fails and completes the given exchange with the specified error
212         */
213        public void fail(MessageExchange exchange, Exception error) throws MessagingException {
214            if (error instanceof FaultException) {
215                FaultException faultException = (FaultException) error;
216                exchange.setFault(faultException.getFault());
217            } else {
218                exchange.setError(error);
219            }
220            getDeliveryChannel().send(exchange);
221        }
222    
223        // Helper methods to make JBI a little more concise to use from a client
224        //-------------------------------------------------------------------------
225        
226        public Object request(Map inMessageProperties, Object content) throws JBIException {
227            return request(null, null, inMessageProperties, content);
228        }
229    
230        public void send(Map inMessageProperties, Object content) throws JBIException {
231            send(null, null, inMessageProperties, content);
232        }
233    
234        public boolean sendSync(Map inMessageProperties, Object content) throws JBIException {
235            return sendSync(null, null, inMessageProperties, content);
236        }
237    
238        public void send(EndpointResolver resolver, Map exchangeProperties, Map inMessageProperties, Object content) throws JBIException {
239            InOnly exchange = createInOnlyExchange(resolver);
240            populateMessage(exchange, exchangeProperties, inMessageProperties, content);
241            send(exchange);
242        }
243    
244        public boolean sendSync(EndpointResolver resolver, Map exchangeProperties, 
245                                Map inMessageProperties, Object content) throws JBIException {
246            InOnly exchange = createInOnlyExchange(resolver);
247            populateMessage(exchange, exchangeProperties, inMessageProperties, content);
248            return sendSync(exchange);
249        }
250    
251        public Object request(EndpointResolver resolver, Map exchangeProperties, Map inMessageProperties, Object content) throws JBIException {
252            InOut exchange = createInOutExchange(resolver);
253            populateMessage(exchange, exchangeProperties, inMessageProperties, content);
254            boolean answer = sendSync(exchange);
255            if (!answer) {
256                throw new JBIException("Exchange aborted");
257            }
258            Exception error = exchange.getError();
259            if (error != null) {
260                throw new JBIException(error);
261            }
262            if (exchange.getFault() != null) {
263                done(exchange);
264                throw FaultException.newInstance(exchange);
265            }
266    
267    
268            NormalizedMessage outMessage = exchange.getOutMessage();
269            if (outMessage == null) {
270                throw new NoOutMessageAvailableException(exchange);
271            }
272            Object result = getMarshaler().unmarshal(exchange, outMessage);
273            done(exchange);
274            return result;
275        }
276    
277        public ServiceEndpoint resolveEndpointReference(String uri) {
278            DocumentFragment epr = URIResolver.createWSAEPR(uri);
279            return getContext().resolveEndpointReference(epr);
280        }
281    
282        public EndpointResolver createResolverForService(QName service) {
283            return new ServiceNameEndpointResolver(service);
284        }
285    
286        public EndpointResolver createResolverInterface(QName interfaceName) {
287            return new InterfaceNameEndpointResolver(interfaceName);
288        }
289    
290        public EndpointResolver createResolverForExternalService(QName service) {
291            return new ExternalServiceNameEndpointResolver(service);
292        }
293    
294        public EndpointResolver createResolverForExternalInterface(QName interfaceName) {
295            return new ExternalInterfaceNameEndpointResolver(interfaceName);
296        }
297    
298        public EndpointResolver createResolverForExternalInterface(QName service, String endpoint) {
299            return new ServiceAndEndpointNameResolver(service, endpoint);
300        }
301        
302        public void close() throws JBIException {
303        }
304    
305    
306        // Properties
307        //-------------------------------------------------------------------------
308        public EndpointFilter getFilter() {
309            return filter;
310        }
311    
312        /**
313         * Sets the filter used to exclude possible endpoints based on their capabilities
314         *
315         * @param filter
316         */
317        public void setFilter(EndpointFilter filter) {
318            this.filter = filter;
319        }
320    
321        public PojoMarshaler getMarshaler() {
322            return marshaler;
323        }
324    
325        /**
326         * Sets the marshaler used to convert objects which are not already JAXP {@link Source} instances
327         * into the normalized message content.
328         *
329         * @param marshaler
330         */
331        public void setMarshaler(PojoMarshaler marshaler) {
332            this.marshaler = marshaler;
333        }
334    
335        // Implementation methods
336        //-------------------------------------------------------------------------
337    
338        protected void configureEndpoint(MessageExchange exchange, EndpointResolver resolver) throws JBIException {
339            if (resolver != null) {
340                exchange.setEndpoint(resolver.resolveEndpoint(getContext(), exchange, filter));
341            }
342        }
343    
344        protected void populateMessage(MessageExchange exchange, Map exchangeProperties, 
345                                       Map inMessageProperties, Object content) throws MessagingException {
346            NormalizedMessage in = exchange.getMessage("in");
347            populateExchangeProperties(exchange, exchangeProperties);
348            populateMessageProperties(in, inMessageProperties);
349            getMarshaler().marshal(exchange, in, content);
350        }
351    
352        protected void populateExchangeProperties(MessageExchange exchange, Map properties) {
353            if (properties != null) {
354                for (Iterator iter = properties.entrySet().iterator(); iter.hasNext();) {
355                    Map.Entry entry = (Map.Entry) iter.next();
356                    exchange.setProperty((String) entry.getKey(), entry.getValue());
357                }
358            }
359        }
360    
361        protected void populateMessageProperties(NormalizedMessage message, Map properties) {
362            if (properties != null) {
363                for (Iterator iter = properties.entrySet().iterator(); iter.hasNext();) {
364                    Map.Entry entry = (Map.Entry) iter.next();
365                    message.setProperty((String) entry.getKey(), entry.getValue());
366                }
367            }
368        }
369    }