You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by ay...@apache.org on 2014/02/28 10:53:23 UTC
git commit: add status+headers in websocket response;
enable all the tests for CXF-5339
Repository: cxf
Updated Branches:
refs/heads/master 2c2c74460 -> 627a47eb6
add status+headers in websocket response; enable all the tests for CXF-5339
Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/627a47eb
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/627a47eb
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/627a47eb
Branch: refs/heads/master
Commit: 627a47eb65a2e8cb3e9ebb6894f29d8fcc407daf
Parents: 2c2c744
Author: Akitoshi Yoshida <ay...@apache.org>
Authored: Fri Feb 28 10:52:46 2014 +0100
Committer: Akitoshi Yoshida <ay...@apache.org>
Committed: Fri Feb 28 10:53:07 2014 +0100
----------------------------------------------------------------------
.../transport/http_jetty/JettyWebSocket.java | 127 ++++++++++++++-----
.../jaxrs/websocket/BookStoreWebSocket.java | 11 +-
.../JAXRSClientServerWebSocketTest.java | 109 ++++++++++++++--
.../jaxrs/websocket/WebSocketTestClient.java | 23 +++-
4 files changed, 222 insertions(+), 48 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cxf/blob/627a47eb/rt/transports/http-jetty/src/main/java/org/apache/cxf/transport/http_jetty/JettyWebSocket.java
----------------------------------------------------------------------
diff --git a/rt/transports/http-jetty/src/main/java/org/apache/cxf/transport/http_jetty/JettyWebSocket.java b/rt/transports/http-jetty/src/main/java/org/apache/cxf/transport/http_jetty/JettyWebSocket.java
index d20d816..30660aa99 100644
--- a/rt/transports/http-jetty/src/main/java/org/apache/cxf/transport/http_jetty/JettyWebSocket.java
+++ b/rt/transports/http-jetty/src/main/java/org/apache/cxf/transport/http_jetty/JettyWebSocket.java
@@ -60,7 +60,8 @@ import org.eclipse.jetty.websocket.WebSocket;
class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessage {
private static final Logger LOG = LogUtils.getL7dLogger(JettyWebSocket.class);
-
+ private static final String CRLF = "\r\n";
+
private JettyHTTPDestination jettyHTTPDestination;
private ServletContext servletContext;
private Connection webSocketConnection;
@@ -142,9 +143,8 @@ class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessa
}
}
- @SuppressWarnings("unchecked")
- <T> T getRequestProperty(String name, Class<T> cls) {
- return (T)requestProperties.get(name);
+ private <T> T getRequestProperty(String name, Class<T> cls) {
+ return getValue(requestProperties, name, cls);
}
private WebSocketVirtualServletRequest createServletRequest(byte[] data, int offset, int length)
@@ -163,14 +163,34 @@ class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessa
* @param offset
* @param length
*/
- public void write(byte[] data, int offset, int length) throws IOException {
+ void write(byte[] data, int offset, int length) throws IOException {
LOG.log(Level.INFO, "write(byte[], offset, length)");
webSocketConnection.sendMessage(data, offset, length);
}
- public ServletOutputStream getServletOutputStream() {
+ private static byte[] buildResponse(Map<String, String> headers, byte[] data, int offset, int length) {
+ StringBuilder sb = new StringBuilder();
+ String v = headers.get("$sc");
+ sb.append(v == null ? "200" : v).append(CRLF);
+ v = headers.get("Content-Type");
+ if (v != null) {
+ sb.append("Content-Type: ").append(v).append(CRLF);
+ }
+ sb.append(CRLF);
+
+ byte[] hb = sb.toString().getBytes();
+ byte[] longdata = new byte[hb.length + length];
+ System.arraycopy(hb, 0, longdata, 0, hb.length);
+ if (data != null && length > 0) {
+ System.arraycopy(data, offset, longdata, hb.length, length);
+ }
+ return longdata;
+ }
+
+ ServletOutputStream getServletOutputStream(final Map<String, String> headers) {
LOG.log(Level.INFO, "getServletOutputStream()");
return new ServletOutputStream() {
+
@Override
public void write(int b) throws IOException {
byte[] data = new byte[1];
@@ -180,14 +200,21 @@ class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessa
@Override
public void write(byte[] data, int offset, int length) throws IOException {
+ if (headers.get("$flushed") == null) {
+ data = buildResponse(headers, data, offset, length);
+ offset = 0;
+ length = data.length;
+ headers.put("$flushed", "true");
+ }
webSocketConnection.sendMessage(data, offset, length);
}
};
}
- public OutputStream getOutputStream() {
+ OutputStream getOutputStream(final Map<String, String> headers) {
LOG.log(Level.INFO, "getServletOutputStream()");
return new OutputStream() {
+
@Override
public void write(int b) throws IOException {
byte[] data = new byte[1];
@@ -197,6 +224,12 @@ class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessa
@Override
public void write(byte[] data, int offset, int length) throws IOException {
+ if (headers.get("$flushed") == null) {
+ data = buildResponse(headers, data, offset, length);
+ offset = 0;
+ length = data.length;
+ headers.put("$flushed", "true");
+ }
webSocketConnection.sendMessage(data, offset, length);
}
};
@@ -219,9 +252,9 @@ class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessa
requestHeaders = readHeaders(in);
String path = requestHeaders.get("$path");
String origin = websocket.getRequestProperty("requestURI", String.class);
- if (path.length() < origin.length()) {
- //TODO use a more appropriate exception (invalidxxx?);
- throw new IOException("invalid path: " + path + " not within " + origin);
+ if (path.startsWith(origin)) {
+ //REVISIT for now, log it here and reject the request later.
+ LOG.log(Level.WARNING, "invalid path: {0} not within {1}", new Object[]{path, origin});
}
}
@@ -518,16 +551,16 @@ class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessa
@Override
public Enumeration<String> getHeaders(String name) {
- // TODO Auto-generated method stub
LOG.log(Level.INFO, "getHeaders");
+ // our protocol assumes no multiple headers
return Collections.enumeration(Arrays.asList(requestHeaders.get(name)));
}
@Override
public int getIntHeader(String name) {
- // TODO Auto-generated method stub
LOG.log(Level.INFO, "getIntHeader");
- return 0;
+ String v = requestHeaders.get(name);
+ return v == null ? -1 : Integer.parseInt(v);
}
@Override
@@ -587,9 +620,16 @@ class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessa
@Override
public StringBuffer getRequestURL() {
LOG.log(Level.INFO, "getRequestURL");
+ String url = websocket.getRequestProperty("requestURL", String.class);
String origin = websocket.getRequestProperty("requestURI", String.class);
StringBuffer sb = new StringBuffer();
- sb.append(origin).append(getRequestURI().substring(origin.length()));
+ String uri = getRequestURI();
+ //REVISIT the way to reject the requeist uri that does not match the original request
+ if (!uri.startsWith(origin)) {
+ sb.append(url).append("invalid").append(uri);
+ } else {
+ sb.append(url).append(uri.substring(origin.length()));
+ }
return sb;
}
@@ -677,20 +717,31 @@ class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessa
LOG.log(Level.INFO, "logout");
}
-
}
//TODO need to make the header setting to be written to the body (as symmetric to the request behavior)
static class WebSocketVirtualServletResponse implements HttpServletResponse {
private JettyWebSocket websocket;
-
+ private Map<String, String> responseHeaders;
+ private boolean flushed;
+
public WebSocketVirtualServletResponse(JettyWebSocket websocket) {
this.websocket = websocket;
+ this.responseHeaders = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
}
@Override
public void flushBuffer() throws IOException {
LOG.log(Level.INFO, "flushBuffer()");
+ if (!flushed) {
+ //REVISIT this mechanism to determine if the headers have been flushed
+ if (responseHeaders.get("$flushed") == null) {
+ byte[] data = buildResponse(responseHeaders, null, 0, 0);
+ websocket.write(data, 0, data.length);
+ responseHeaders.put("$flushed", "true");
+ }
+ flushed = true;
+ }
}
@Override
@@ -708,9 +759,8 @@ class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessa
@Override
public String getContentType() {
- // TODO Auto-generated method stub
LOG.log(Level.INFO, "getContentType()");
- return null;
+ return responseHeaders.get("Content-Type");
}
@Override
@@ -723,13 +773,13 @@ class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessa
@Override
public ServletOutputStream getOutputStream() throws IOException {
LOG.log(Level.INFO, "getOutputStream()");
- return websocket.getServletOutputStream();
+ return websocket.getServletOutputStream(responseHeaders);
}
@Override
public PrintWriter getWriter() throws IOException {
LOG.log(Level.INFO, "getWriter()");
- return new PrintWriter(websocket.getOutputStream());
+ return new PrintWriter(websocket.getOutputStream(responseHeaders));
}
@Override
@@ -763,18 +813,18 @@ class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessa
@Override
public void setContentLength(int len) {
- // TODO
if (LOG.isLoggable(Level.INFO)) {
LOG.log(Level.INFO, "setContentLength({0})", len);
}
+ responseHeaders.put("Content-Length", Integer.toString(len));
}
@Override
public void setContentType(String type) {
- // TODO Auto-generated method stub
if (LOG.isLoggable(Level.INFO)) {
LOG.log(Level.INFO, "setContentType({0})", type);
}
+ responseHeaders.put("Content-Type", type);
}
@Override
@@ -801,18 +851,18 @@ class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessa
@Override
public void addHeader(String name, String value) {
- // TODO
if (LOG.isLoggable(Level.INFO)) {
LOG.log(Level.INFO, "addHeader({0}, {1})", new Object[]{name, value});
}
+ responseHeaders.put(name, value);
}
@Override
public void addIntHeader(String name, int value) {
- // TODO
if (LOG.isLoggable(Level.INFO)) {
LOG.log(Level.INFO, "addIntHeader({0}, {1})", new Object[]{name, value});
}
+ responseHeaders.put(name, Integer.toString(value));
}
@Override
@@ -887,25 +937,26 @@ class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessa
@Override
public int getStatus() {
- // TODO Auto-generated method stub
LOG.log(Level.INFO, "getStatus()");
- return 0;
+ String v = responseHeaders.get("$sc");
+ return v == null ? 200 : Integer.parseInt(v);
}
@Override
public void sendError(int sc) throws IOException {
- // TODO Auto-generated method stub
if (LOG.isLoggable(Level.INFO)) {
LOG.log(Level.INFO, "sendError{0}", sc);
}
+ responseHeaders.put("$sc", Integer.toString(sc));
}
@Override
public void sendError(int sc, String msg) throws IOException {
- // TODO Auto-generated method stub
if (LOG.isLoggable(Level.INFO)) {
LOG.log(Level.INFO, "sendError({0}, {1})", new Object[]{sc, msg});
}
+ responseHeaders.put("$sc", Integer.toString(sc));
+ responseHeaders.put("$sm", msg);
}
@Override
@@ -942,17 +993,19 @@ class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessa
@Override
public void setStatus(int sc) {
- // TODO Auto-generated method stub
if (LOG.isLoggable(Level.INFO)) {
LOG.log(Level.INFO, "setStatus({0})", sc);
}
+ responseHeaders.put("$sc", Integer.toString(sc));
}
@Override
public void setStatus(int sc, String sm) {
- // TODO Auto-generated method stub
- LOG.log(Level.INFO, "setStatus({0}, {1})", new Object[]{sc, sm});
-
+ if (LOG.isLoggable(Level.INFO)) {
+ LOG.log(Level.INFO, "setStatus({0}, {1})", new Object[]{sc, sm});
+ }
+ responseHeaders.put("$sc", Integer.toString(sc));
+ responseHeaders.put("$sm", sm);
}
}
@@ -987,9 +1040,15 @@ class JettyWebSocket implements WebSocket.OnBinaryMessage, WebSocket.OnTextMessa
return headers;
}
- ///// this is copied from AttachmentDeserializer. we may think about putting this method to IOUtils
+ @SuppressWarnings("unchecked")
+ private static <T> T getValue(Map<String, Object> properties, String name, Class<T> cls) {
+ return (T)properties.get(name);
+ }
+
+
+ ///// this is copied from AttachmentDeserializer with a minor change. think about putting this method to IOUtils
private static String readLine(InputStream in) throws IOException {
- StringBuffer buffer = new StringBuffer(128);
+ StringBuilder buffer = new StringBuilder(128);
int c;
http://git-wip-us.apache.org/repos/asf/cxf/blob/627a47eb/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/BookStoreWebSocket.java
----------------------------------------------------------------------
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/BookStoreWebSocket.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/BookStoreWebSocket.java
index 40ffee3..e658ca6 100644
--- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/BookStoreWebSocket.java
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/BookStoreWebSocket.java
@@ -22,6 +22,8 @@ package org.apache.cxf.systest.jaxrs.websocket;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
@@ -38,6 +40,8 @@ import org.apache.cxf.systest.jaxrs.Book;
@Path("/web/bookstore")
public class BookStoreWebSocket {
+ private static ExecutorService executor = Executors.newSingleThreadExecutor();
+
@GET
@Path("/booknames")
@@ -50,7 +54,8 @@ public class BookStoreWebSocket {
@Path("/booknames/servletstream")
@Produces("text/plain")
public void getBookNameStream(@Context HttpServletResponse response) throws Exception {
- OutputStream os = response.getOutputStream();
+ OutputStream os = response.getOutputStream();
+ response.setContentType("text/plain");
os.write("CXF in Action".getBytes());
os.flush();
}
@@ -78,7 +83,7 @@ public class BookStoreWebSocket {
public void write(final OutputStream out) throws IOException, WebApplicationException {
out.write(("Today: " + new java.util.Date()).getBytes());
// just for testing, using a thread
- new Thread(new Runnable() {
+ executor.execute(new Runnable() {
public void run() {
try {
for (int r = 2, i = 1; i <= 5; r *= 2, i++) {
@@ -89,7 +94,7 @@ public class BookStoreWebSocket {
e.printStackTrace();
}
}
- }).start();
+ });
}
};
}
http://git-wip-us.apache.org/repos/asf/cxf/blob/627a47eb/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/JAXRSClientServerWebSocketTest.java
----------------------------------------------------------------------
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/JAXRSClientServerWebSocketTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/JAXRSClientServerWebSocketTest.java
index be9058d..583f3a4 100644
--- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/JAXRSClientServerWebSocketTest.java
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/JAXRSClientServerWebSocketTest.java
@@ -27,7 +27,6 @@ import org.apache.cxf.jaxrs.model.AbstractResourceInfo;
import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
import org.junit.BeforeClass;
-import org.junit.Ignore;
import org.junit.Test;
public class JAXRSClientServerWebSocketTest extends AbstractBusClientServerTestBase {
@@ -55,7 +54,10 @@ public class JAXRSClientServerWebSocketTest extends AbstractBusClientServerTestB
assertTrue("one book must be returned", wsclient.await(3));
List<byte[]> received = wsclient.getReceivedBytes();
assertEquals(1, received.size());
- String value = new String(received.get(0));
+ Response resp = new Response(received.get(0));
+ assertEquals(200, resp.getStatusCode());
+ assertEquals("text/plain", resp.getContentType());
+ String value = new String(resp.getEntity());
assertEquals("CXF in Action", value);
// call another GET service
@@ -63,7 +65,10 @@ public class JAXRSClientServerWebSocketTest extends AbstractBusClientServerTestB
wsclient.sendMessage("GET /web/bookstore/books/123".getBytes());
assertTrue("response expected", wsclient.await(3));
received = wsclient.getReceivedBytes();
- value = new String(received.get(0));
+ resp = new Response(received.get(0));
+ assertEquals(200, resp.getStatusCode());
+ assertEquals("application/xml", resp.getContentType());
+ value = new String(resp.getEntity());
assertTrue(value.startsWith("<?xml ") && value.endsWith("</Book>"));
// call the POST service
@@ -71,17 +76,25 @@ public class JAXRSClientServerWebSocketTest extends AbstractBusClientServerTestB
wsclient.sendMessage("POST /web/bookstore/booksplain\r\nContent-Type: text/plain\r\n\r\n123".getBytes());
assertTrue("response expected", wsclient.await(3));
received = wsclient.getReceivedBytes();
- value = new String(received.get(0));
+ resp = new Response(received.get(0));
+ assertEquals(200, resp.getStatusCode());
+ assertEquals("text/plain", resp.getContentType());
+ value = new String(resp.getEntity());
assertEquals("123", value);
// call the GET service returning a continous stream output
wsclient.reset(6);
wsclient.sendMessage("GET /web/bookstore/bookbought".getBytes());
- assertTrue("wrong method, no response expected", wsclient.await(5));
+ assertTrue("response expected", wsclient.await(5));
received = wsclient.getReceivedBytes();
assertEquals(6, received.size());
- assertTrue((new String(received.get(0))).startsWith("Today:"));
+ resp = new Response(received.get(0));
+ assertEquals(200, resp.getStatusCode());
+ assertEquals("application/octet-stream", resp.getContentType());
+ value = new String(resp.getEntity());
+ assertTrue(value.startsWith("Today:"));
for (int r = 2, i = 1; i < 6; r *= 2, i++) {
+ // subsequent data should not carry headers
assertEquals(r, Integer.parseInt(new String(received.get(i))));
}
} finally {
@@ -100,7 +113,10 @@ public class JAXRSClientServerWebSocketTest extends AbstractBusClientServerTestB
assertTrue("one book must be returned", wsclient.await(3));
List<byte[]> received = wsclient.getReceivedBytes();
assertEquals(1, received.size());
- String value = new String(received.get(0));
+ Response resp = new Response(received.get(0));
+ assertEquals(200, resp.getStatusCode());
+ assertEquals("text/plain", resp.getContentType());
+ String value = new String(resp.getEntity());
assertEquals("CXF in Action", value);
} finally {
wsclient.close();
@@ -108,7 +124,6 @@ public class JAXRSClientServerWebSocketTest extends AbstractBusClientServerTestB
}
@Test
- @Ignore
public void testWrongMethod() throws Exception {
String address = "ws://localhost:" + PORT + "/web/bookstore";
@@ -117,13 +132,17 @@ public class JAXRSClientServerWebSocketTest extends AbstractBusClientServerTestB
try {
// call the GET service using POST
wsclient.sendMessage("POST /web/bookstore/booknames".getBytes());
+ assertTrue("error response expected", wsclient.await(3));
+ List<byte[]> received = wsclient.getReceivedBytes();
+ assertEquals(1, received.size());
+ Response resp = new Response(received.get(0));
+ assertEquals(405, resp.getStatusCode());
} finally {
wsclient.close();
}
}
@Test
- @Ignore
public void testPathRestriction() throws Exception {
String address = "ws://localhost:" + PORT + "/web/bookstore";
@@ -132,9 +151,81 @@ public class JAXRSClientServerWebSocketTest extends AbstractBusClientServerTestB
try {
// call the GET service over the different path
wsclient.sendMessage("GET /bookstore2".getBytes());
+ assertTrue("error response expected", wsclient.await(3));
+ List<byte[]> received = wsclient.getReceivedBytes();
+ assertEquals(1, received.size());
+ Response resp = new Response(received.get(0));
+ assertEquals(404, resp.getStatusCode());
} finally {
wsclient.close();
}
}
+
+ //TODO this is a temporary way to verify the response; we should come up with something better.
+ private static class Response {
+ private byte[] data;
+ private int pos;
+ private int statusCode;
+ private String contentType;
+ private byte[] entity;
+
+ public Response(byte[] data) {
+ this.data = data;
+ String line = readLine();
+ statusCode = Integer.parseInt(line);
+ while ((line = readLine()) != null) {
+ if (line.length() > 0) {
+ int del = line.indexOf(':');
+ String h = line.substring(0, del).trim();
+ String v = line.substring(del + 1).trim();
+ if ("Content-Type".equalsIgnoreCase(h)) {
+ contentType = v;
+ }
+ }
+ }
+ entity = new byte[data.length - pos];
+ System.arraycopy(data, pos, entity, 0, entity.length);
+ }
+
+
+
+ public int getStatusCode() {
+ return statusCode;
+ }
+ public String getContentType() {
+ return contentType;
+ }
+
+ public byte[] getEntity() {
+ return entity;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Status: ").append(statusCode).append("\r\n");
+ sb.append("Type: ").append(contentType).append("\r\n");
+ sb.append("Entity: ").append(new String(entity)).append("\r\n");
+ return sb.toString();
+ }
+
+ private String readLine() {
+ StringBuilder sb = new StringBuilder();
+ while (pos < data.length) {
+ int c = 0xff & data[pos++];
+ if (c == '\n') {
+ break;
+ } else if (c == '\r') {
+ continue;
+ } else {
+ sb.append((char)c);
+ }
+ }
+ if (sb.length() == 0) {
+ return null;
+ }
+ return sb.toString();
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/cxf/blob/627a47eb/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/WebSocketTestClient.java
----------------------------------------------------------------------
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/WebSocketTestClient.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/WebSocketTestClient.java
index 24af539..6dff1ac 100644
--- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/WebSocketTestClient.java
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/WebSocketTestClient.java
@@ -147,9 +147,28 @@ class WebSocketTestClient {
StringBuilder xbuf = new StringBuilder().append("\nHEX: ");
StringBuilder cbuf = new StringBuilder().append("\nASC: ");
for (byte b : data) {
- xbuf.append(Integer.toHexString(0xff & b)).append(' ');
- cbuf.append((0x80 & b) != 0 ? '.' : (char)b).append(" ");
+ writeHex(xbuf, 0xff & b);
+ writePrintable(cbuf, 0xff & b);
}
return xbuf.append(cbuf);
}
+
+ private static void writeHex(StringBuilder buf, int b) {
+ buf.append(Integer.toHexString(0x100 | (0xff & b)).substring(1)).append(' ');
+ }
+
+ private static void writePrintable(StringBuilder buf, int b) {
+ if (b == 0x0d) {
+ buf.append("\\r");
+ } else if (b == 0x0a) {
+ buf.append("\\n");
+ } else if (b == 0x09) {
+ buf.append("\\t");
+ } else if ((0x80 & b) != 0) {
+ buf.append('.').append(' ');
+ } else {
+ buf.append((char)b).append(' ');
+ }
+ buf.append(' ');
+ }
}