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 * @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}