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}