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 */ 017package org.apache.activemq.transport.stomp; 018 019import java.io.UnsupportedEncodingException; 020import java.util.Arrays; 021import java.util.HashMap; 022import java.util.Locale; 023import java.util.Map; 024 025import org.apache.activemq.command.Command; 026import org.apache.activemq.command.Endpoint; 027import org.apache.activemq.command.Response; 028import org.apache.activemq.state.CommandVisitor; 029import org.apache.activemq.util.MarshallingSupport; 030 031/** 032 * Represents all the data in a STOMP frame. 033 * 034 * @author <a href="http://hiramchirino.com">chirino</a> 035 */ 036public class StompFrame implements Command { 037 038 public static final byte[] NO_DATA = new byte[] {}; 039 040 private String action; 041 private Map<String, String> headers = new HashMap<String, String>(); 042 private byte[] content = NO_DATA; 043 044 private transient Object transportContext = null; 045 046 public StompFrame(String command) { 047 this(command, null, null); 048 } 049 050 public StompFrame(String command, Map<String, String> headers) { 051 this(command, headers, null); 052 } 053 054 public StompFrame(String command, Map<String, String> headers, byte[] data) { 055 this.action = command; 056 if (headers != null) 057 this.headers = headers; 058 if (data != null) 059 this.content = data; 060 } 061 062 public StompFrame() { 063 } 064 065 public String getAction() { 066 return action; 067 } 068 069 public void setAction(String command) { 070 this.action = command; 071 } 072 073 public byte[] getContent() { 074 return content; 075 } 076 077 public String getBody() { 078 try { 079 return new String(content, "UTF-8"); 080 } catch (UnsupportedEncodingException e) { 081 return new String(content); 082 } 083 } 084 085 public void setContent(byte[] data) { 086 this.content = data; 087 } 088 089 public Map<String, String> getHeaders() { 090 return headers; 091 } 092 093 public void setHeaders(Map<String, String> headers) { 094 this.headers = headers; 095 } 096 097 @Override 098 public int getCommandId() { 099 return 0; 100 } 101 102 @Override 103 public Endpoint getFrom() { 104 return null; 105 } 106 107 @Override 108 public Endpoint getTo() { 109 return null; 110 } 111 112 @Override 113 public boolean isBrokerInfo() { 114 return false; 115 } 116 117 @Override 118 public boolean isMessage() { 119 return false; 120 } 121 122 @Override 123 public boolean isMessageAck() { 124 return false; 125 } 126 127 @Override 128 public boolean isMessageDispatch() { 129 return false; 130 } 131 132 @Override 133 public boolean isMessageDispatchNotification() { 134 return false; 135 } 136 137 @Override 138 public boolean isResponse() { 139 return false; 140 } 141 142 @Override 143 public boolean isResponseRequired() { 144 return false; 145 } 146 147 @Override 148 public boolean isShutdownInfo() { 149 return false; 150 } 151 152 @Override 153 public boolean isConnectionControl() { 154 return false; 155 } 156 157 @Override 158 public boolean isConsumerControl() { 159 return false; 160 } 161 162 @Override 163 public boolean isWireFormatInfo() { 164 return false; 165 } 166 167 @Override 168 public void setCommandId(int value) { 169 } 170 171 @Override 172 public void setFrom(Endpoint from) { 173 } 174 175 @Override 176 public void setResponseRequired(boolean responseRequired) { 177 } 178 179 @Override 180 public void setTo(Endpoint to) { 181 } 182 183 @Override 184 public Response visit(CommandVisitor visitor) throws Exception { 185 return null; 186 } 187 188 @Override 189 public byte getDataStructureType() { 190 return 0; 191 } 192 193 @Override 194 public boolean isMarshallAware() { 195 return false; 196 } 197 198 @Override 199 public String toString() { 200 return format(true); 201 } 202 203 public String format() { 204 return format(false); 205 } 206 207 public String format(boolean forLogging) { 208 if( !forLogging && getAction().equals(Stomp.Commands.KEEPALIVE) ) { 209 return "\n"; 210 } 211 StringBuilder buffer = new StringBuilder(); 212 buffer.append(getAction()); 213 buffer.append("\n"); 214 Map<String, String> headers = getHeaders(); 215 for (Map.Entry<String, String> entry : headers.entrySet()) { 216 buffer.append(entry.getKey()); 217 buffer.append(":"); 218 if (forLogging && entry.getKey().toString().toLowerCase(Locale.ENGLISH).contains(Stomp.Headers.Connect.PASSCODE)) { 219 buffer.append("*****"); 220 } else { 221 buffer.append(entry.getValue()); 222 } 223 buffer.append("\n"); 224 } 225 buffer.append("\n"); 226 if (getContent() != null) { 227 try { 228 String contentString = new String(getContent(), "UTF-8"); 229 if (forLogging) { 230 contentString = MarshallingSupport.truncate64(contentString); 231 } 232 buffer.append(contentString); 233 } catch (Throwable e) { 234 buffer.append(Arrays.toString(getContent())); 235 } 236 } 237 // terminate the frame 238 buffer.append('\u0000'); 239 return buffer.toString(); 240 } 241 242 /** 243 * Transports may wish to associate additional data with the connection. For 244 * example, an SSL transport may use this field to attach the client 245 * certificates used when the connection was established. 246 * 247 * @return the transport context. 248 */ 249 public Object getTransportContext() { 250 return transportContext; 251 } 252 253 /** 254 * Transports may wish to associate additional data with the connection. For 255 * example, an SSL transport may use this field to attach the client 256 * certificates used when the connection was established. 257 * 258 * @param transportContext value used to set the transport context 259 */ 260 public void setTransportContext(Object transportContext) { 261 this.transportContext = transportContext; 262 } 263}