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.jbi.messaging;
018    
019    import java.io.Externalizable;
020    import java.io.IOException;
021    import java.io.ObjectInput;
022    import java.io.ObjectOutput;
023    import java.net.URI;
024    import java.util.Comparator;
025    import java.util.Set;
026    
027    import javax.jbi.messaging.ExchangeStatus;
028    import javax.jbi.messaging.Fault;
029    import javax.jbi.messaging.MessageExchange;
030    import javax.jbi.messaging.MessagingException;
031    import javax.jbi.messaging.NormalizedMessage;
032    import javax.jbi.servicedesc.ServiceEndpoint;
033    import javax.transaction.Transaction;
034    import javax.xml.namespace.QName;
035    import javax.xml.transform.dom.DOMSource;
036    
037    import org.w3c.dom.Node;
038    
039    import org.apache.commons.logging.Log;
040    import org.apache.commons.logging.LogFactory;
041    import org.apache.servicemix.JbiConstants;
042    import org.apache.servicemix.jbi.container.ActivationSpec;
043    import org.apache.servicemix.jbi.framework.ComponentContextImpl;
044    import org.apache.servicemix.jbi.framework.ComponentNameSpace;
045    import org.apache.servicemix.jbi.jaxp.SourceTransformer;
046    
047    /**
048     * A simple message exchange declaration. This is partial, just giving us enough
049     * ME function for the doodle. This doesn't add anything new to the current
050     * MessageExchange definition.
051     * 
052     * @version $Revision: 564607 $
053     */
054    public abstract class MessageExchangeImpl implements MessageExchange, Externalizable {
055    
056        public static final String IN = "in";
057    
058        public static final String OUT = "out";
059    
060        public static final String FAULT = "fault";
061    
062        public static final int MAX_MSG_DISPLAY_SIZE = 1500;
063    
064        public static final boolean PRESERVE_CONTENT = Boolean.getBoolean("org.apache.servicemix.preserveContent");
065    
066        public static final int SYNC_STATE_ASYNC = 0;
067    
068        public static final int SYNC_STATE_SYNC_SENT = 1;
069    
070        public static final int SYNC_STATE_SYNC_RECEIVED = 2;
071    
072        /**
073         * Exchange is not transactional
074         */
075        public static final int TX_STATE_NONE = 0;
076    
077        /**
078         * Exchange has been enlisted in the current transaction. This means that
079         * the transaction must be commited for the exchange to be delivered.
080         */
081        public static final int TX_STATE_ENLISTED = 1;
082    
083        /**
084         * Transaction is being conveyed by the exchange. The transaction context
085         * will be given to the target component.
086         */
087        public static final int TX_STATE_CONVEYED = 2;
088    
089        protected static final int CAN_SET_IN_MSG = 0x00000001;
090    
091        protected static final int CAN_SET_OUT_MSG = 0x00000002;
092    
093        protected static final int CAN_SET_FAULT_MSG = 0x00000004;
094    
095        protected static final int CAN_PROVIDER = 0x00000008;
096    
097        protected static final int CAN_CONSUMER = 0x00000000;
098    
099        protected static final int CAN_SEND = 0x00000010;
100    
101        protected static final int CAN_STATUS_ACTIVE = 0x00000040;
102    
103        protected static final int CAN_STATUS_DONE = 0x00000080;
104    
105        protected static final int CAN_STATUS_ERROR = 0x00000100;
106    
107        protected static final int CAN_OWNER = 0x00000200;
108    
109        protected static final int STATES_CANS = 0;
110    
111        protected static final int STATES_NEXT_OUT = 1;
112    
113        protected static final int STATES_NEXT_FAULT = 2;
114    
115        protected static final int STATES_NEXT_ERROR = 3;
116    
117        protected static final int STATES_NEXT_DONE = 4;
118    
119        private static final long serialVersionUID = -3639175136897005605L;
120    
121        private static final Log LOG = LogFactory.getLog(MessageExchangeImpl.class);
122    
123        protected ComponentContextImpl sourceContext;
124    
125        protected ExchangePacket packet;
126    
127        protected PojoMarshaler marshaler;
128    
129        protected int state;
130    
131        protected int syncState = SYNC_STATE_ASYNC;
132    
133        protected int txState = TX_STATE_NONE;
134    
135        protected int[][] states;
136    
137        protected MessageExchangeImpl mirror;
138    
139        protected transient boolean pushDeliver;
140    
141        protected transient Object txLock;
142    
143        protected transient String key;
144    
145        /**
146         * Constructor
147         * 
148         * @param exchangeId
149         * @param pattern
150         */
151        public MessageExchangeImpl(String exchangeId, URI pattern, int[][] states) {
152            this.states = states;
153            this.packet = new ExchangePacket();
154            this.packet.setExchangeId(exchangeId);
155            this.packet.setPattern(pattern);
156        }
157    
158        protected MessageExchangeImpl(ExchangePacket packet, int[][] states) {
159            this.states = states;
160            this.packet = packet;
161        }
162    
163        protected MessageExchangeImpl() {
164        }
165    
166        protected void copyFrom(MessageExchangeImpl me) {
167            if (this != me) {
168                this.packet = me.packet;
169                this.state = me.state;
170                this.mirror.packet = me.packet;
171                this.mirror.state = me.mirror.state;
172            }
173        }
174    
175        protected boolean can(int c) {
176            return (this.states[state][STATES_CANS] & c) == c;
177        }
178    
179        /**
180         * Returns the activation spec that was provided when the component was
181         * registered
182         * 
183         * @return the spec
184         */
185        public ActivationSpec getActivationSpec() {
186            if (sourceContext != null) {
187                return sourceContext.getActivationSpec();
188            }
189            return null;
190        }
191    
192        /**
193         * Returns the context which created the message exchange which can then be
194         * used for routing
195         * 
196         * @return the context
197         */
198        public ComponentContextImpl getSourceContext() {
199            return sourceContext;
200        }
201    
202        /**
203         * Set the context
204         * 
205         * @param sourceContext
206         */
207        public void setSourceContext(ComponentContextImpl sourceContext) {
208            this.sourceContext = sourceContext;
209            this.mirror.sourceContext = sourceContext;
210        }
211    
212        /**
213         * @return the packet
214         */
215        public ExchangePacket getPacket() {
216            return packet;
217        }
218    
219        /**
220         * @return URI of pattern exchange
221         */
222        public URI getPattern() {
223            return packet.getPattern();
224        }
225    
226        /**
227         * @return the exchange Id
228         */
229        public String getExchangeId() {
230            return packet.getExchangeId();
231        }
232    
233        /**
234         * @return the processing status of the exchange
235         */
236        public ExchangeStatus getStatus() {
237            if (this.packet.isAborted()) {
238                return ExchangeStatus.ERROR;
239            }
240            return this.packet.getStatus();
241        }
242    
243        /**
244         * set the processing status
245         * 
246         * @param exchangeStatus
247         * @throws MessagingException
248         */
249        public void setStatus(ExchangeStatus exchangeStatus) throws MessagingException {
250            if (!can(CAN_OWNER)) {
251                throw new IllegalStateException("component is not owner");
252            }
253            this.packet.setStatus(exchangeStatus);
254    
255        }
256    
257        /**
258         * set the source of a failure
259         * 
260         * @param exception
261         */
262        public void setError(Exception exception) {
263            if (!can(CAN_OWNER)) {
264                throw new IllegalStateException("component is not owner when trying to set error: " + exception, exception);
265            }
266            this.packet.setError(exception);
267        }
268    
269        /**
270         * @return the exception describing a processing error
271         */
272        public Exception getError() {
273            return packet.getError();
274        }
275    
276        /**
277         * @return the fault message for an exchange
278         */
279        public Fault getFault() {
280            return packet.getFault();
281        }
282    
283        /**
284         * set the fault message for the exchange
285         * 
286         * @param fault
287         * @throws MessagingException
288         */
289        public void setFault(Fault fault) throws MessagingException {
290            setMessage(fault, FAULT);
291        }
292    
293        /**
294         * @return a new message
295         * @throws MessagingException
296         */
297        public NormalizedMessage createMessage() throws MessagingException {
298            return new NormalizedMessageImpl(this);
299        }
300    
301        /**
302         * factory method for fault objects
303         * 
304         * @return a new fault
305         * @throws MessagingException
306         */
307        public Fault createFault() throws MessagingException {
308            return new FaultImpl();
309        }
310    
311        /**
312         * get a NormalizedMessage based on the message reference
313         * 
314         * @param name
315         * @return a NormalizedMessage
316         */
317        public NormalizedMessage getMessage(String name) {
318            if (IN.equals(name)) {
319                return packet.getIn();
320            } else if (OUT.equals(name)) {
321                return packet.getOut();
322            } else if (FAULT.equals(name)) {
323                return packet.getFault();
324            } else {
325                return null;
326            }
327        }
328    
329        /**
330         * set a NormalizedMessage with a named reference
331         * 
332         * @param message
333         * @param name
334         * @throws MessagingException
335         */
336        public void setMessage(NormalizedMessage message, String name) throws MessagingException {
337            if (!can(CAN_OWNER)) {
338                throw new IllegalStateException("component is not owner");
339            }
340            if (message == null) {
341                throw new IllegalArgumentException("message should not be null");
342            }
343            if (name == null) {
344                throw new IllegalArgumentException("name should not be null");
345            }
346            if (IN.equalsIgnoreCase(name)) {
347                if (!can(CAN_SET_IN_MSG)) {
348                    throw new MessagingException("In not supported");
349                }
350                if (packet.getIn() != null) {
351                    throw new MessagingException("In message is already set");
352                }
353                ((NormalizedMessageImpl) message).exchange = this;
354                packet.setIn((NormalizedMessageImpl) message);
355            } else if (OUT.equalsIgnoreCase(name)) {
356                if (!can(CAN_SET_OUT_MSG)) {
357                    throw new MessagingException("Out not supported");
358                }
359                if (packet.getOut() != null) {
360                    throw new MessagingException("Out message is already set");
361                }
362                ((NormalizedMessageImpl) message).exchange = this;
363                packet.setOut((NormalizedMessageImpl) message);
364            } else if (FAULT.equalsIgnoreCase(name)) {
365                if (!can(CAN_SET_FAULT_MSG)) {
366                    throw new MessagingException("Fault not supported");
367                }
368                if (!(message instanceof Fault)) {
369                    throw new MessagingException("Setting fault, but message is not a fault");
370                }
371                if (packet.getFault() != null) {
372                    throw new MessagingException("Fault message is already set");
373                }
374                ((NormalizedMessageImpl) message).exchange = this;
375                packet.setFault((FaultImpl) message);
376            } else {
377                throw new MessagingException("Message name must be in, out or fault");
378            }
379        }
380    
381        /**
382         * @param name
383         * @return the property from the exchange
384         */
385        public Object getProperty(String name) {
386            if (JTA_TRANSACTION_PROPERTY_NAME.equals(name)) {
387                return packet.getTransactionContext();
388            } else if (JbiConstants.PERSISTENT_PROPERTY_NAME.equals(name)) {
389                return packet.getPersistent();
390            } else {
391                return packet.getProperty(name);
392            }
393        }
394    
395        /**
396         * set a named property on the exchange
397         * 
398         * @param name
399         * @param value
400         */
401        public void setProperty(String name, Object value) {
402            if (!can(CAN_OWNER)) {
403                throw new IllegalStateException("component is not owner");
404            }
405            if (name == null) {
406                throw new IllegalArgumentException("name should not be null");
407            }
408            if (JTA_TRANSACTION_PROPERTY_NAME.equals(name)) {
409                packet.setTransactionContext((Transaction) value);
410            } else if (JbiConstants.PERSISTENT_PROPERTY_NAME.equals(name)) {
411                packet.setPersistent((Boolean) value);
412            } else {
413                packet.setProperty(name, value);
414            }
415        }
416    
417        /**
418         * @return property names
419         */
420        public Set getPropertyNames() {
421            return packet.getPropertyNames();
422        }
423    
424        /**
425         * Set an endpoint
426         * 
427         * @param endpoint
428         */
429        public void setEndpoint(ServiceEndpoint endpoint) {
430            packet.setEndpoint(endpoint);
431        }
432    
433        /**
434         * set a service
435         * 
436         * @param name
437         */
438        public void setService(QName name) {
439            packet.setServiceName(name);
440        }
441    
442        /**
443         * set an operation
444         * 
445         * @param name
446         */
447        public void setOperation(QName name) {
448            packet.setOperationName(name);
449        }
450    
451        /**
452         * set an interface
453         * 
454         * @param name
455         */
456        public void setInterfaceName(QName name) {
457            packet.setInterfaceName(name);
458        }
459    
460        /**
461         * @return the endpoint
462         */
463        public ServiceEndpoint getEndpoint() {
464            return packet.getEndpoint();
465        }
466    
467        /**
468         * @return the service
469         */
470        public QName getService() {
471            return packet.getServiceName();
472        }
473    
474        /**
475         * @return the interface name
476         */
477        public QName getInterfaceName() {
478            return packet.getInterfaceName();
479        }
480    
481        /**
482         * @return the operation
483         */
484        public QName getOperation() {
485            return packet.getOperationName();
486        }
487    
488        /**
489         * @return the transaction context
490         */
491        public Transaction getTransactionContext() {
492            return packet.getTransactionContext();
493        }
494    
495        /**
496         * set the transaction
497         * 
498         * @param transaction
499         * @throws MessagingException
500         */
501        public void setTransactionContext(Transaction transaction) throws MessagingException {
502            packet.setTransactionContext(transaction);
503        }
504    
505        /**
506         * @return true if transacted
507         */
508        public boolean isTransacted() {
509            return this.packet.getTransactionContext() != null;
510        }
511    
512        /**
513         * @return the Role of this exchange
514         */
515        public Role getRole() {
516            return can(CAN_PROVIDER) ? Role.PROVIDER : Role.CONSUMER;
517        }
518    
519        /**
520         * @return the in message
521         */
522        public NormalizedMessage getInMessage() {
523            return this.packet.getIn();
524        }
525    
526        /**
527         * set the in message
528         * 
529         * @param message
530         * @throws MessagingException
531         */
532        public void setInMessage(NormalizedMessage message) throws MessagingException {
533            setMessage(message, IN);
534        }
535    
536        /**
537         * @return the out message
538         */
539        public NormalizedMessage getOutMessage() {
540            return getMessage(OUT);
541        }
542    
543        /**
544         * set the out message
545         * 
546         * @param message
547         * @throws MessagingException
548         */
549        public void setOutMessage(NormalizedMessage message) throws MessagingException {
550            setMessage(message, OUT);
551        }
552    
553        /**
554         * @return Returns the sourceId.
555         */
556        public ComponentNameSpace getSourceId() {
557            return packet.getSourceId();
558        }
559    
560        /**
561         * @param sourceId
562         *            The sourceId to set.
563         */
564        public void setSourceId(ComponentNameSpace sourceId) {
565            packet.setSourceId(sourceId);
566        }
567    
568        /**
569         * @return Returns the destinationId.
570         */
571        public ComponentNameSpace getDestinationId() {
572            return packet.getDestinationId();
573        }
574    
575        /**
576         * @param destinationId
577         *            The destinationId to set.
578         */
579        public void setDestinationId(ComponentNameSpace destinationId) {
580            packet.setDestinationId(destinationId);
581        }
582    
583        public Boolean getPersistent() {
584            return packet.getPersistent();
585        }
586    
587        public void setPersistent(Boolean persistent) {
588            packet.setPersistent(persistent);
589        }
590    
591        public PojoMarshaler getMarshaler() {
592            if (marshaler == null) {
593                marshaler = new DefaultMarshaler();
594            }
595            return marshaler;
596        }
597    
598        public void setMarshaler(PojoMarshaler marshaler) {
599            this.marshaler = marshaler;
600        }
601    
602        public abstract void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
603    
604        public void writeExternal(ObjectOutput out) throws IOException {
605            packet.writeExternal(out);
606            out.write(state);
607            out.write(mirror.state);
608            out.writeBoolean(can(CAN_PROVIDER));
609        }
610    
611        public void handleSend(boolean sync) throws MessagingException {
612            // Check if send / sendSync is legal
613            if (!can(CAN_SEND)) {
614                throw new MessagingException("illegal call to send / sendSync");
615            }
616            if (sync && getStatus() != ExchangeStatus.ACTIVE) {
617                throw new MessagingException("illegal call to sendSync");
618            }
619            this.syncState = sync ? SYNC_STATE_SYNC_SENT : SYNC_STATE_ASYNC;
620            // Check status
621            ExchangeStatus status = getStatus();
622            if (status == ExchangeStatus.ACTIVE && !can(CAN_STATUS_ACTIVE)) {
623                throw new MessagingException("illegal exchange status: active");
624            }
625            if (status == ExchangeStatus.DONE && !can(CAN_STATUS_DONE)) {
626                throw new MessagingException("illegal exchange status: done");
627            }
628            if (status == ExchangeStatus.ERROR && !can(CAN_STATUS_ERROR)) {
629                throw new MessagingException("illegal exchange status: error");
630            }
631            // Check message
632            // Change state
633            if (status == ExchangeStatus.ACTIVE && packet.getFault() == null) {
634                this.state = this.states[this.state][STATES_NEXT_OUT];
635            } else if (status == ExchangeStatus.ACTIVE && packet.getFault() != null) {
636                this.state = this.states[this.state][STATES_NEXT_FAULT];
637            } else if (status == ExchangeStatus.ERROR) {
638                this.state = this.states[this.state][STATES_NEXT_ERROR];
639            } else if (status == ExchangeStatus.DONE) {
640                this.state = this.states[this.state][STATES_NEXT_DONE];
641            } else {
642                throw new IllegalStateException("unknown status");
643            }
644            if (this.state < 0 || this.state >= this.states.length) {
645                throw new IllegalStateException("next state is illegal");
646            }
647        }
648    
649        public void handleAccept() throws MessagingException {
650            // Change state
651            ExchangeStatus status = getStatus();
652            int nextState;
653            if (status == ExchangeStatus.ACTIVE && packet.getFault() == null) {
654                nextState = this.states[this.state][STATES_NEXT_OUT];
655            } else if (status == ExchangeStatus.ACTIVE && packet.getFault() != null) {
656                nextState = this.states[this.state][STATES_NEXT_FAULT];
657            } else if (status == ExchangeStatus.ERROR) {
658                nextState = this.states[this.state][STATES_NEXT_ERROR];
659            } else if (status == ExchangeStatus.DONE) {
660                nextState = this.states[this.state][STATES_NEXT_DONE];
661            } else {
662                throw new IllegalStateException("unknown status");
663            }
664            if (nextState < 0 || nextState >= this.states.length) {
665                throw new IllegalStateException("next state is illegal");
666            }
667            this.state = nextState;
668        }
669    
670        public MessageExchangeImpl getMirror() {
671            return mirror;
672        }
673    
674        public int getSyncState() {
675            return syncState;
676        }
677    
678        public void setSyncState(int syncState) {
679            this.syncState = syncState;
680        }
681    
682        /**
683         * @return the txState
684         */
685        public int getTxState() {
686            return txState;
687        }
688    
689        /**
690         * @param txState
691         *            the txState to set
692         */
693        public void setTxState(int txState) {
694            this.txState = txState;
695        }
696    
697        public boolean isPushDelivery() {
698            return this.pushDeliver;
699        }
700    
701        public void setPushDeliver(boolean b) {
702            this.pushDeliver = true;
703        }
704    
705        /**
706         * @return the txLock
707         */
708        public Object getTxLock() {
709            return txLock;
710        }
711    
712        /**
713         * @param txLock
714         *            the txLock to set
715         */
716        public void setTxLock(Object txLock) {
717            this.txLock = txLock;
718        }
719    
720        public String toString() {
721            try {
722                StringBuffer sb = new StringBuffer();
723                String name = getClass().getName();
724                name = name.substring(name.lastIndexOf('.') + 1, name.length() - 4);
725                sb.append(name);
726                sb.append("[\n");
727                sb.append("  id: ").append(getExchangeId()).append('\n');
728                sb.append("  status: ").append(getStatus()).append('\n');
729                sb.append("  role: ").append(getRole() == Role.CONSUMER ? "consumer" : "provider").append('\n');
730                if (getInterfaceName() != null) {
731                    sb.append("  interface: ").append(getInterfaceName()).append('\n');
732                }
733                if (getService() != null) {
734                    sb.append("  service: ").append(getService()).append('\n');
735                }
736                if (getEndpoint() != null) {
737                    sb.append("  endpoint: ").append(getEndpoint().getEndpointName()).append('\n');
738                }
739                if (getOperation() != null) {
740                    sb.append("  operation: ").append(getOperation()).append('\n');
741                }
742                SourceTransformer st = new SourceTransformer();
743                display("in", sb, st);
744                display("out", sb, st);
745                display("fault", sb, st);
746                if (getError() != null) {
747                    sb.append("  error: ");
748                    sb.append(getError());
749                    sb.append('\n');
750                }
751                sb.append("]");
752                return sb.toString();
753            } catch (Exception e) {
754                LOG.trace("Error caught in toString", e);
755                return super.toString();
756            }
757        }
758    
759        private void display(String msg, StringBuffer sb, SourceTransformer st) {
760            if (getMessage(msg) != null) {
761                sb.append("  ").append(msg).append(": ");
762                try {
763                    if (getMessage(msg).getContent() != null) {
764                        if (PRESERVE_CONTENT) {
765                            sb.append(getMessage(msg).getContent().getClass());
766                        } else {
767                            Node node = st.toDOMNode(getMessage(msg).getContent());
768                            getMessage(msg).setContent(new DOMSource(node));
769                            String str = st.toString(node);
770                            if (str.length() > MAX_MSG_DISPLAY_SIZE) {
771                                sb.append(str.substring(0, MAX_MSG_DISPLAY_SIZE)).append("...");
772                            } else {
773                                sb.append(str);
774                            }
775                        }
776                    } else {
777                        sb.append("null");
778                    }
779                } catch (Exception e) {
780                    sb.append("Unable to display: ").append(e);
781                }
782                sb.append('\n');
783            }
784        }
785    
786        /**
787         * Compute a unique key for this exchange proxy. It has to be different for
788         * the two sides of the exchange, so we include the role + the exchange id.
789         * Obviously, it works, because the role never change for a given proxy.
790         * 
791         * @return
792         */
793        public String getKey() {
794            if (key == null) {
795                key = (getRole() == Role.CONSUMER ? "consumer:" : "provider:") + getExchangeId();
796            }
797            return key;
798        }
799    
800        /**
801         * Comparator that can be used to sort exchanges according to their "age" in
802         * their processing: i.e.: a newly created exchange will be more than a DONE
803         * exchange ... If the arguments are not instances of MessageExchangeImpl,
804         * returns 0.
805         */
806        public static class AgeComparator implements Comparator<MessageExchangeImpl> {
807            public int compare(MessageExchangeImpl m0, MessageExchangeImpl m1) {
808                int i0 = (m0.state * 4) / (m0.states.length - 1);
809                int i1 = (m1.state * 4) / (m1.states.length - 1);
810                if (i0 < i1) {
811                    return +1;
812                } else if (i0 == i1) {
813                    return 0;
814                } else {
815                    return -1;
816                }
817            }
818        }
819    }