View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.messaging.simp;
18  
19  import java.security.Principal;
20  import java.util.List;
21  import java.util.Map;
22  
23  import org.springframework.messaging.Message;
24  import org.springframework.messaging.support.IdTimestampMessageHeaderInitializer;
25  import org.springframework.messaging.support.MessageHeaderAccessor;
26  import org.springframework.messaging.support.NativeMessageHeaderAccessor;
27  import org.springframework.util.Assert;
28  import org.springframework.util.CollectionUtils;
29  
30  /**
31   * A base class for working with message headers in simple messaging protocols that
32   * support basic messaging patterns. Provides uniform access to specific values common
33   * across protocols such as a destination, message type (e.g. publish, subscribe, etc),
34   * session id, and others.
35   *
36   * <p>Use one of the static factory method in this class, then call getters and setters,
37   * and at the end if necessary call {@link #toMap()} to obtain the updated headers.
38   *
39   * @author Rossen Stoyanchev
40   * @since 4.0
41   */
42  public class SimpMessageHeaderAccessor extends NativeMessageHeaderAccessor {
43  
44  	private static final IdTimestampMessageHeaderInitializer headerInitializer;
45  
46  	static {
47  		headerInitializer = new IdTimestampMessageHeaderInitializer();
48  		headerInitializer.setDisableIdGeneration();
49  		headerInitializer.setEnableTimestamp(false);
50  	}
51  
52  	// SiMP header names
53  
54  	public static final String CONNECT_MESSAGE_HEADER = "simpConnectMessage";
55  
56  	public static final String DESTINATION_HEADER = "simpDestination";
57  
58  	public static final String MESSAGE_TYPE_HEADER = "simpMessageType";
59  
60  	public static final String SESSION_ID_HEADER = "simpSessionId";
61  
62  	public static final String SESSION_ATTRIBUTES = "simpSessionAttributes";
63  
64  	public static final String SUBSCRIPTION_ID_HEADER = "simpSubscriptionId";
65  
66  	public static final String USER_HEADER = "simpUser";
67  
68  	/**
69  	 * For internal use.
70  	 * <p>The original destination used by a client when subscribing. Such a
71  	 * destination may have been modified (e.g. user destinations) on the server
72  	 * side. This header provides a hint so messages sent to clients may have
73  	 * a destination matching to their original subscription.
74  	 */
75  	public static final String ORIGINAL_DESTINATION = "simpOrigDestination";
76  
77  
78  	/**
79  	 * A constructor for creating new message headers.
80  	 * This constructor is protected. See factory methods in this and sub-classes.
81  	 */
82  	protected SimpMessageHeaderAccessor(SimpMessageType messageType, Map<String, List<String>> externalSourceHeaders) {
83  		super(externalSourceHeaders);
84  		Assert.notNull(messageType, "MessageType must not be null");
85  		setHeader(MESSAGE_TYPE_HEADER, messageType);
86  		headerInitializer.initHeaders(this);
87  	}
88  
89  	/**
90  	 * A constructor for accessing and modifying existing message headers. This
91  	 * constructor is protected. See factory methods in this and sub-classes.
92  	 */
93  	protected SimpMessageHeaderAccessor(Message<?> message) {
94  		super(message);
95  		headerInitializer.initHeaders(this);
96  	}
97  
98  
99  	@Override
100 	protected MessageHeaderAccessor createAccessor(Message<?> message) {
101 		return wrap(message);
102 	}
103 
104 	public void setMessageTypeIfNotSet(SimpMessageType messageType) {
105 		if (getMessageType() == null) {
106 			setHeader(MESSAGE_TYPE_HEADER, messageType);
107 		}
108 	}
109 
110 	public SimpMessageType getMessageType() {
111 		return (SimpMessageType) getHeader(MESSAGE_TYPE_HEADER);
112 	}
113 
114 	public void setDestination(String destination) {
115 		Assert.notNull(destination, "Destination must not be null");
116 		setHeader(DESTINATION_HEADER, destination);
117 	}
118 
119 	public String getDestination() {
120 		return (String) getHeader(DESTINATION_HEADER);
121 	}
122 
123 	public void setSubscriptionId(String subscriptionId) {
124 		setHeader(SUBSCRIPTION_ID_HEADER, subscriptionId);
125 	}
126 
127 	public String getSubscriptionId() {
128 		return (String) getHeader(SUBSCRIPTION_ID_HEADER);
129 	}
130 
131 	public void setSessionId(String sessionId) {
132 		setHeader(SESSION_ID_HEADER, sessionId);
133 	}
134 
135 	/**
136 	 * @return the id of the current session
137 	 */
138 	public String getSessionId() {
139 		return (String) getHeader(SESSION_ID_HEADER);
140 	}
141 
142 	/**
143 	 * A static alternative for access to the session attributes header.
144 	 */
145 	public void setSessionAttributes(Map<String, Object> attributes) {
146 		setHeader(SESSION_ATTRIBUTES, attributes);
147 	}
148 
149 	/**
150 	 * Return the attributes associated with the current session.
151 	 */
152 	@SuppressWarnings("unchecked")
153 	public Map<String, Object> getSessionAttributes() {
154 		return (Map<String, Object>) getHeader(SESSION_ATTRIBUTES);
155 	}
156 
157 	public void setUser(Principal principal) {
158 		setHeader(USER_HEADER, principal);
159 	}
160 
161 	/**
162 	 * Return the user associated with the current session.
163 	 */
164 	public Principal getUser() {
165 		return (Principal) getHeader(USER_HEADER);
166 	}
167 
168 	@Override
169 	public String getShortLogMessage(Object payload) {
170 		if (getMessageType() == null) {
171 			return super.getDetailedLogMessage(payload);
172 		}
173 		StringBuilder sb = getBaseLogMessage();
174 		if (!CollectionUtils.isEmpty(getSessionAttributes())) {
175 			sb.append(" attributes[").append(getSessionAttributes().size()).append("]");
176 		}
177 		sb.append(getShortPayloadLogMessage(payload));
178 		return sb.toString();
179 	}
180 
181 	@SuppressWarnings("unchecked")
182 	@Override
183 	public String getDetailedLogMessage(Object payload) {
184 		if (getMessageType() == null) {
185 			return super.getDetailedLogMessage(payload);
186 		}
187 		StringBuilder sb = getBaseLogMessage();
188 		if (!CollectionUtils.isEmpty(getSessionAttributes())) {
189 			sb.append(" attributes=").append(getSessionAttributes());
190 		}
191 		if (!CollectionUtils.isEmpty((Map<String, List<String>>) getHeader(NATIVE_HEADERS))) {
192 			sb.append(" nativeHeaders=").append(getHeader(NATIVE_HEADERS));
193 		}
194 		sb.append(getDetailedPayloadLogMessage(payload));
195 		return sb.toString();
196 	}
197 
198 	private StringBuilder getBaseLogMessage() {
199 		StringBuilder sb = new StringBuilder();
200 		sb.append(getMessageType().name());
201 		if (getDestination() != null) {
202 			sb.append(" destination=").append(getDestination());
203 		}
204 		if (getSubscriptionId() != null) {
205 			sb.append(" subscriptionId=").append(getSubscriptionId());
206 		}
207 		sb.append(" session=").append(getSessionId());
208 		if (getUser() != null) {
209 			sb.append(" user=").append(getUser().getName());
210 		}
211 		return sb;
212 	}
213 
214 
215 	// Static factory methods and accessors
216 
217 	/**
218 	 * Create an instance with
219 	 * {@link org.springframework.messaging.simp.SimpMessageType} {@code MESSAGE}.
220 	 */
221 	public static SimpMessageHeaderAccessor create() {
222 		return new SimpMessageHeaderAccessor(SimpMessageType.MESSAGE, null);
223 	}
224 
225 	/**
226 	 * Create an instance with the given
227 	 * {@link org.springframework.messaging.simp.SimpMessageType}.
228 	 */
229 	public static SimpMessageHeaderAccessor create(SimpMessageType messageType) {
230 		return new SimpMessageHeaderAccessor(messageType, null);
231 	}
232 
233 	/**
234 	 * Create an instance from the payload and headers of the given Message.
235 	 */
236 	public static SimpMessageHeaderAccessor wrap(Message<?> message) {
237 		return new SimpMessageHeaderAccessor(message);
238 	}
239 
240 	public static SimpMessageType getMessageType(Map<String, Object> headers) {
241 		return (SimpMessageType) headers.get(MESSAGE_TYPE_HEADER);
242 	}
243 
244 	public static String getDestination(Map<String, Object> headers) {
245 		return (String) headers.get(DESTINATION_HEADER);
246 	}
247 
248 	public static String getSubscriptionId(Map<String, Object> headers) {
249 		return (String) headers.get(SUBSCRIPTION_ID_HEADER);
250 	}
251 
252 	public static String getSessionId(Map<String, Object> headers) {
253 		return (String) headers.get(SESSION_ID_HEADER);
254 	}
255 
256 	@SuppressWarnings("unchecked")
257 	public static Map<String, Object> getSessionAttributes(Map<String, Object> headers) {
258 		return (Map<String, Object>) headers.get(SESSION_ATTRIBUTES);
259 	}
260 
261 	public static Principal getUser(Map<String, Object> headers) {
262 		return (Principal) headers.get(USER_HEADER);
263 	}
264 
265 }