/*
*
* Copyright (C) 2012-2014 R T Huitema. All Rights Reserved.
* Web: www.42.co.nz
* Email: robert@42.co.nz
* Author: R T Huitema
*
* This file is part of the signalk-server-java project
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* 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 nz.co.fortytwo.signalk.client;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;
import org.apache.activemq.transport.stomp.StompFrame;
import org.apache.activemq.transport.stomp.StompWireFormat;
public class StompConnection {
public static final long RECEIVE_TIMEOUT = 10000L;
private Socket stompSocket;
private ByteArrayOutputStream inputBuffer;
private String version;
public StompConnection() {
this.inputBuffer = new ByteArrayOutputStream();
this.version = "1.0";
}
public void open(String host, int port) throws IOException, UnknownHostException {
open(new Socket(host, port));
}
public void open(Socket socket) {
this.stompSocket = socket;
}
public void close() throws IOException {
if (this.stompSocket != null) {
this.stompSocket.close();
this.stompSocket = null;
}
}
public void sendFrame(String data) throws Exception {
byte[] bytes = data.getBytes("UTF-8");
OutputStream outputStream = this.stompSocket.getOutputStream();
outputStream.write(bytes);
outputStream.flush();
}
public void sendFrame(String frame, byte[] data) throws Exception {
byte[] bytes = frame.getBytes("UTF-8");
OutputStream outputStream = this.stompSocket.getOutputStream();
outputStream.write(bytes);
outputStream.write(data);
outputStream.flush();
}
public StompFrame receive() throws Exception {
return receive(10000L);
}
public StompFrame receive(long timeOut) throws Exception {
this.stompSocket.setSoTimeout((int) timeOut);
InputStream is = this.stompSocket.getInputStream();
StompWireFormat wf = new StompWireFormat();
wf.setStompVersion(this.version);
DataInputStream dis = new DataInputStream(is);
return ((StompFrame) wf.unmarshal(dis));
}
public String receiveFrame() throws Exception {
return receiveFrame(10000L);
}
public String receiveFrame(long timeOut) throws Exception {
this.stompSocket.setSoTimeout((int) timeOut);
InputStream is = this.stompSocket.getInputStream();
int c = 0;
while (true) {
c = is.read();
if (c < 0)
throw new IOException("socket closed.");
if (c == 0) {
c = is.read();
if (c == 10) {
return stringFromBuffer(this.inputBuffer);
}
this.inputBuffer.write(0);
this.inputBuffer.write(c);
}
this.inputBuffer.write(c);
}
}
private String stringFromBuffer(ByteArrayOutputStream inputBuffer) throws Exception {
byte[] ba = inputBuffer.toByteArray();
inputBuffer.reset();
return new String(ba, "UTF-8");
}
public Socket getStompSocket() {
return this.stompSocket;
}
public void setStompSocket(Socket stompSocket) {
this.stompSocket = stompSocket;
}
public StompFrame connect(String username, String password) throws Exception {
return connect(username, password, null);
}
public StompFrame connect(String username, String password, String client) throws Exception {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("login", username);
headers.put("passcode", password);
if (client != null) {
headers.put("client-id", client);
}
return connect(headers);
}
public StompFrame connect(HashMap<String, String> headers) throws Exception {
StompFrame frame = new StompFrame("CONNECT", headers);
sendFrame(frame.format());
StompFrame connect = receive();
if (!(connect.getAction().equals("CONNECTED")))
throw new Exception(new StringBuilder().append("Not connected: ").append(connect.getBody()).toString());
return connect;
}
public void disconnect() throws Exception {
disconnect(null);
}
public void disconnect(String receiptId) throws Exception {
StompFrame frame = new StompFrame("DISCONNECT");
if ((receiptId != null) && (!(receiptId.isEmpty()))) {
frame.getHeaders().put("receipt", receiptId);
}
sendFrame(frame.format());
}
public void send(String destination, String message) throws Exception {
send(destination, message, null, null);
}
public void send(String destination, String message, String transaction, HashMap<String, String> headers) throws Exception {
if (headers == null) {
headers = new HashMap<String, String>();
}
headers.put("destination", destination);
if (transaction != null) {
headers.put("transaction", transaction);
}
StompFrame frame = new StompFrame("SEND", headers, message.getBytes());
sendFrame(frame.format());
}
public void subscribe(String destination) throws Exception {
subscribe(destination, null, null);
}
public void subscribe(String destination, String ack) throws Exception {
subscribe(destination, ack, new HashMap<String, String>());
}
public void subscribe(String destination, String ack, HashMap<String, String> headers) throws Exception {
if (headers == null) {
headers = new HashMap<String, String>();
}
headers.put("destination", destination);
if (ack != null) {
headers.put("ack", ack);
}
StompFrame frame = new StompFrame("SUBSCRIBE", headers);
sendFrame(frame.format());
}
public void unsubscribe(String destination) throws Exception {
unsubscribe(destination, null);
}
public void unsubscribe(String destination, HashMap<String, String> headers) throws Exception {
if (headers == null) {
headers = new HashMap<String, String>();
}
headers.put("destination", destination);
StompFrame frame = new StompFrame("UNSUBSCRIBE", headers);
sendFrame(frame.format());
}
public void begin(String transaction) throws Exception {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("transaction", transaction);
StompFrame frame = new StompFrame("BEGIN", headers);
sendFrame(frame.format());
}
public void abort(String transaction) throws Exception {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("transaction", transaction);
StompFrame frame = new StompFrame("ABORT", headers);
sendFrame(frame.format());
}
public void commit(String transaction) throws Exception {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("transaction", transaction);
StompFrame frame = new StompFrame("COMMIT", headers);
sendFrame(frame.format());
}
public void ack(StompFrame frame) throws Exception {
ack((String) frame.getHeaders().get("message-id"), null);
}
public void ack(StompFrame frame, String transaction) throws Exception {
ack((String) frame.getHeaders().get("message-id"), transaction);
}
public void ack(String messageId) throws Exception {
ack(messageId, null);
}
public void ack(String messageId, String transaction) throws Exception {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("message-id", messageId);
if (transaction != null)
headers.put("transaction", transaction);
StompFrame frame = new StompFrame("ACK", headers);
sendFrame(frame.format());
}
public void keepAlive() throws Exception {
OutputStream outputStream = this.stompSocket.getOutputStream();
outputStream.write(10);
outputStream.flush();
}
protected String appendHeaders(HashMap<String, Object> headers) {
StringBuilder result = new StringBuilder();
for (String key : headers.keySet()) {
result.append(new StringBuilder().append(key).append(":").append(headers.get(key)).append("\n").toString());
}
result.append("\n");
return result.toString();
}
public String getVersion() {
return this.version;
}
public void setVersion(String version) {
this.version = version;
}
}