Add SockJsFrameType enum

SPR-10797
This commit is contained in:
Rossen Stoyanchev
2014-04-21 13:57:09 -04:00
parent c86f711de5
commit c14ba1a0ff
8 changed files with 219 additions and 27 deletions

View File

@@ -19,6 +19,10 @@ package org.springframework.web.socket.sockjs.frame;
import org.springframework.util.Assert;
/**
* A default implementation of
* {@link org.springframework.web.socket.sockjs.frame.SockJsFrameFormat} that relies
* on {@link java.lang.String#format(String, Object...)}..
*
* @author Rossen Stoyanchev
* @since 4.0
*/
@@ -33,14 +37,9 @@ public class DefaultSockJsFrameFormat implements SockJsFrameFormat {
}
/**
* @param frame the SockJs frame.
* @return new SockJsFrame instance with the formatted content
*/
@Override
public SockJsFrame format(SockJsFrame frame) {
String content = String.format(this.format, preProcessContent(frame.getContent()));
return new SockJsFrame(content);
public String format(SockJsFrame frame) {
return String.format(this.format, preProcessContent(frame.getContent()));
}
protected String preProcessContent(String content) {

View File

@@ -18,18 +18,17 @@ package org.springframework.web.socket.sockjs.frame;
import java.nio.charset.Charset;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Represents a SockJS frame. Provides factory methods to create SockJS frames on
* the server side.
* Represents a SockJS frame. Provides factory methods to create SockJS frames.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class SockJsFrame {
private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
public static final Charset CHARSET = Charset.forName("UTF-8");
private static final SockJsFrame OPEN_FRAME = new SockJsFrame("o");
@@ -40,12 +39,40 @@ public class SockJsFrame {
private static final SockJsFrame CLOSE_ANOTHER_CONNECTION_OPEN_FRAME = closeFrame(2010, "Another connection still open");
private final SockJsFrameType type;
private final String content;
/**
* Create a new instance frame with the given frame content.
* @param content the content, must be a non-empty and represent a valid SockJS frame
*/
public SockJsFrame(String content) {
Assert.notNull("Content must not be null");
this.content = content;
StringUtils.hasText(content);
if ("o".equals(content)) {
this.type = SockJsFrameType.OPEN;
this.content = content;
}
else if ("h".equals(content)) {
this.type = SockJsFrameType.HEARTBEAT;
this.content = content;
}
else if (content.charAt(0) == 'a') {
this.type = SockJsFrameType.MESSAGE;
this.content = (content.length() > 1 ? content : "a[]");
}
else if (content.charAt(0) == 'm') {
this.type = SockJsFrameType.MESSAGE;
this.content = (content.length() > 1 ? content : "null");
}
else if (content.charAt(0) == 'c') {
this.type = SockJsFrameType.CLOSE;
this.content = (content.length() > 1 ? content : "c[]");
}
else {
throw new IllegalArgumentException("Unexpected SockJS frame type in content=\"" + content + "\"");
}
}
public static SockJsFrame openFrame() {
@@ -74,12 +101,39 @@ public class SockJsFrame {
}
/**
* Return the SockJS frame type.
*/
public SockJsFrameType getType() {
return this.type;
}
/**
* Return the SockJS frame content, never {@code null}.
*/
public String getContent() {
return this.content;
}
/**
* Return the SockJS frame content as a byte array.
*/
public byte[] getContentBytes() {
return this.content.getBytes(UTF8_CHARSET);
return this.content.getBytes(CHARSET);
}
/**
* Return data contained in a SockJS "message" and "close" frames. Otherwise
* for SockJS "open" and "close" frames, which do not contain data, return
* {@code null}.
*/
public String getFrameData() {
if (SockJsFrameType.OPEN == getType() || SockJsFrameType.HEARTBEAT == getType()) {
return null;
}
else {
return getContent().substring(1);
}
}

View File

@@ -17,11 +17,22 @@
package org.springframework.web.socket.sockjs.frame;
/**
* Applies a transport-specific format to the content of a SockJS frame resulting
* in a content that can be written out. Primarily for use in HTTP server-side
* transports that push data.
*
* <p>Formatting may vary from simply appending a new line character for XHR
* polling and streaming transports, to a jsonp-style callback function,
* surrounding script tags, and more.
*
* <p>For the various SockJS frame formats in use, see implementations of
* {@link org.springframework.web.socket.sockjs.transport.handler.AbstractHttpSendingTransportHandler#getFrameFormat(org.springframework.http.server.ServerHttpRequest) AbstractHttpSendingTransportHandler.getFrameFormat}
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public interface SockJsFrameFormat {
SockJsFrame format(SockJsFrame frame);
String format(SockJsFrame frame);
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.frame;
/**
* SockJS frame types.
*
* @author Rossen Stoyanchev
* @since 4.1
*/
public enum SockJsFrameType {
OPEN, HEARTBEAT, MESSAGE, CLOSE
}

View File

@@ -82,12 +82,12 @@ public abstract class AbstractHttpSendingTransportHandler extends AbstractTransp
}
else {
logger.debug("another " + getTransportType() + " connection still open: " + sockJsSession);
SockJsFrame frame = getFrameFormat(request).format(SockJsFrame.closeFrameAnotherConnectionOpen());
String formattedFrame = getFrameFormat(request).format(SockJsFrame.closeFrameAnotherConnectionOpen());
try {
response.getBody().write(frame.getContentBytes());
response.getBody().write(formattedFrame.getBytes(SockJsFrame.CHARSET));
}
catch (IOException ex) {
throw new SockJsException("Failed to send " + frame, sockJsSession.getId(), ex);
throw new SockJsException("Failed to send " + formattedFrame, sockJsSession.getId(), ex);
}
}
}

View File

@@ -351,11 +351,11 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
@Override
protected void writeFrameInternal(SockJsFrame frame) throws IOException {
if (isActive()) {
frame = this.frameFormat.format(frame);
String formattedFrame = this.frameFormat.format(frame);
if (logger.isTraceEnabled()) {
logger.trace("Writing " + frame);
logger.trace("Writing " + formattedFrame);
}
getResponse().getBody().write(frame.getContentBytes());
getResponse().getBody().write(formattedFrame.getBytes(SockJsFrame.CHARSET));
}
}