001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.jms.listener.adapter;
018
019import javax.jms.Destination;
020import javax.jms.JMSException;
021import javax.jms.Session;
022
023import org.springframework.jms.support.destination.DestinationResolver;
024import org.springframework.lang.Nullable;
025import org.springframework.util.Assert;
026
027/**
028 * Return type of any JMS listener method used to indicate the actual response
029 * destination alongside the response itself. Typically used when said destination
030 * needs to be computed at runtime.
031 *
032 * <p>The example below sends a response with the content of the {@code result}
033 * argument to the {@code queueOut Queue}:
034 *
035 * <pre class="code">
036 * package com.acme.foo;
037 *
038 * public class MyService {
039 *     &#064;JmsListener
040 *     public JmsResponse process(String msg) {
041 *         // process incoming message
042 *         return JmsResponse.forQueue(result, "queueOut");
043 *     }
044 * }</pre>
045 *
046 * If the destination does not need to be computed at runtime,
047 * {@link org.springframework.messaging.handler.annotation.SendTo @SendTo}
048 * is the recommended declarative approach.
049 *
050 * @author Stephane Nicoll
051 * @since 4.2
052 * @param <T> the type of the response
053 * @see org.springframework.jms.annotation.JmsListener
054 * @see org.springframework.messaging.handler.annotation.SendTo
055 */
056public class JmsResponse<T> {
057
058        private final T response;
059
060        private final Object destination;
061
062
063        /**
064         * Create a new {@link JmsResponse} instance.
065         * @param response the content of the result
066         * @param destination the destination
067         */
068        protected JmsResponse(T response, Object destination) {
069                Assert.notNull(response, "Result must not be null");
070                this.response = response;
071                this.destination = destination;
072        }
073
074
075        /**
076         * Return the content of the response.
077         */
078        public T getResponse() {
079                return this.response;
080        }
081
082        /**
083         * Resolve the {@link Destination} to use for this instance. The {@link DestinationResolver}
084         * and {@link Session} can be used to resolve a destination at runtime.
085         * @param destinationResolver the destination resolver to use if necessary
086         * @param session the session to use, if necessary
087         * @return the {@link Destination} to use
088         * @throws JMSException if the DestinationResolver failed to resolve the destination
089         */
090        @Nullable
091        public Destination resolveDestination(DestinationResolver destinationResolver, Session session)
092                        throws JMSException {
093
094                if (this.destination instanceof Destination) {
095                        return (Destination) this.destination;
096                }
097                if (this.destination instanceof DestinationNameHolder) {
098                        DestinationNameHolder nameHolder = (DestinationNameHolder) this.destination;
099                        return destinationResolver.resolveDestinationName(session,
100                                        nameHolder.destinationName, nameHolder.pubSubDomain);
101                }
102                return null;
103        }
104
105        @Override
106        public String toString() {
107                return "JmsResponse [" + "response=" + this.response + ", destination=" + this.destination + ']';
108        }
109
110
111        /**
112         * Create a {@link JmsResponse} targeting the queue with the specified name.
113         */
114        public static <T> JmsResponse<T> forQueue(T result, String queueName) {
115                Assert.notNull(queueName, "Queue name must not be null");
116                return new JmsResponse<>(result, new DestinationNameHolder(queueName, false));
117        }
118
119        /**
120         * Create a {@link JmsResponse} targeting the topic with the specified name.
121         */
122        public static <T> JmsResponse<T> forTopic(T result, String topicName) {
123                Assert.notNull(topicName, "Topic name must not be null");
124                return new JmsResponse<>(result, new DestinationNameHolder(topicName, true));
125        }
126
127        /**
128         * Create a {@link JmsResponse} targeting the specified {@link Destination}.
129         */
130        public static <T> JmsResponse<T> forDestination(T result, Destination destination) {
131                Assert.notNull(destination, "Destination must not be null");
132                return new JmsResponse<>(result, destination);
133        }
134
135
136        /**
137         * Internal class combining a destination name
138         * and its target destination type (queue or topic).
139         */
140        private static class DestinationNameHolder {
141
142                private final String destinationName;
143
144                private final boolean pubSubDomain;
145
146                public DestinationNameHolder(String destinationName, boolean pubSubDomain) {
147                        this.destinationName = destinationName;
148                        this.pubSubDomain = pubSubDomain;
149                }
150
151                @Override
152                public String toString() {
153                        return this.destinationName + "{" + "pubSubDomain=" + this.pubSubDomain + '}';
154                }
155        }
156
157}