/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat, Inc., and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.jboss.narayana.blacktie.jatmibroker.core.transport.hybrid.stomp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jboss.narayana.blacktie.jatmibroker.core.transport.JtsTransactionImple;
import org.jboss.narayana.blacktie.jatmibroker.core.transport.Sender;
import org.jboss.narayana.blacktie.jatmibroker.xatmi.Connection;
import org.jboss.narayana.blacktie.jatmibroker.xatmi.ConnectionException;
public class StompSenderImpl implements Sender {
private static int counter = 0;
private static final Logger log = LogManager.getLogger(StompSenderImpl.class);
private boolean closed;
private String destinationName;
private Socket socket;
private OutputStream outputStream;
private InputStream inputStream;
private Map<String, Sender> conversationalMap;
private String serviceName;
public StompSenderImpl(String serviceName, boolean conversational, String type, Map<String, Sender> conversationalMap,
Properties properties) throws ConnectionException, IOException {
String qtype = "/queue/";
if (type != null) {
qtype = "/" + type + "/";
}
if (conversational) {
this.destinationName = qtype + "BTC_" + serviceName;
} else {
this.destinationName = qtype + "BTR_" + serviceName;
}
String host = (String) properties.get("StompConnectHost");
int port = Integer.parseInt((String) properties.get("StompConnectPort"));
String username = (String) properties.get("StompConnectUsr");
String password = (String) properties.get("StompConnectPwd");
this.socket = StompManagement.connect(host, port, username, password);
this.serviceName = serviceName;
this.outputStream = socket.getOutputStream();
this.inputStream = socket.getInputStream();
this.conversationalMap = conversationalMap;
log.debug("Sender Created: " + destinationName);
}
/**
* Don't want send and close at the same time
*/
public synchronized void send(Object replyTo, short rval, int rcode, byte[] data, int len, int correlationId, int flags,
int ttl, String type, String subtype) throws ConnectionException {
if (closed) {
throw new ConnectionException(Connection.TPEPROTO, "Sender closed");
}
if (data == null) {
data = new byte[1];
len = 1;
}
if (len < 1) {
throw new ConnectionException(Connection.TPEINVAL, "Length of buffer must be greater than 0");
}
log.debug("Sender sending: " + destinationName);
Message message = new Message();
message.setCommand("SEND");
Map<String, String> headers = new HashMap<String, String>();
try {
String ior = JtsTransactionImple.getTransactionIOR();
if (ior != null) {
headers.put("messagecontrol", ior);
log.debug("Sender sending IOR: " + ior);
}
} catch (Exception e) {
throw new ConnectionException(Connection.TPETRAN, e.getMessage());
}
if (replyTo != null) {
log.debug("Reply to: " + replyTo);
headers.put("messagereplyto", (String) replyTo);
}
headers.put("servicename", destinationName);
headers.put("messagecorrelationId", String.valueOf(correlationId));
headers.put("messageflags", String.valueOf(flags));
headers.put("messagerval", String.valueOf(rval));
headers.put("messagercode", String.valueOf(rcode));
headers.put("messagetype", type == null ? "" : type);
headers.put("messagesubtype", subtype == null ? "" : subtype);
if (ttl > 0) {
headers.put("expires", String.valueOf(ttl));
log.debug("EXPIRES: " + headers.get("expires"));
}
synchronized (StompSenderImpl.class) {
headers.put("receipt", "send-J-" + counter);
log.debug("RECEIPT: " + headers.get("receipt"));
counter++;
}
headers.put("destination", destinationName);
message.setHeaders(headers);
byte[] toSend = new byte[len];
if (data != null) {
int min = Math.min(toSend.length, data.length);
System.arraycopy(data, 0, toSend, 0, min);
headers.put("content-length", String.valueOf(toSend.length));
}
message.setBody(toSend);
Message ack;
try {
StompManagement.send(message, this.outputStream);
ack = StompManagement.receive(socket, this.inputStream);
} catch (IOException e) {
throw new ConnectionException(Connection.TPEOS, e.getMessage());
}
if (!ack.getCommand().equals("RECEIPT")) {
log.error(new String(ack.getBody()));
throw new ConnectionException(Connection.TPENOENT, new String(ack.getBody()));
}
log.debug("sent message");
}
/**
* Don't want send and close at the same time
*/
public synchronized void close() throws ConnectionException {
log.debug("Sender closing: " + destinationName);
if (closed) {
throw new ConnectionException(Connection.TPEPROTO, "Sender already closed");
}
closed = true;
try {
log.debug("closing socket: " + socket);
StompManagement.close(socket, outputStream, inputStream);
socket.close();
log.debug("closed socket: " + socket);
conversationalMap.remove(serviceName);
} catch (Throwable t) {
throw new ConnectionException(Connection.TPESYSTEM, "Could not send the message", t);
}
}
public Object getSendTo() {
return destinationName;
}
public Object getEndpoint() {
return socket;
}
}