/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY 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 along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.narayana.blacktie.jatmibroker.core.transport.hybrid;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jboss.narayana.blacktie.jatmibroker.core.ResponseMonitor;
import org.jboss.narayana.blacktie.jatmibroker.core.server.SocketServer;
import org.jboss.narayana.blacktie.jatmibroker.core.transport.EventListener;
import org.jboss.narayana.blacktie.jatmibroker.core.transport.JtsTransactionImple;
import org.jboss.narayana.blacktie.jatmibroker.core.transport.Message;
import org.jboss.narayana.blacktie.jatmibroker.core.transport.Receiver;
import org.jboss.narayana.blacktie.jatmibroker.core.tx.TransactionException;
import org.jboss.narayana.blacktie.jatmibroker.core.tx.TransactionImpl;
import org.jboss.narayana.blacktie.jatmibroker.xatmi.Connection;
import org.jboss.narayana.blacktie.jatmibroker.xatmi.ConnectionException;
/**
* @author zhfeng
*
*/
public class SocketReceiverImpl implements Receiver, Runnable {
private static final Logger log = LogManager.getLogger(SocketReceiverImpl.class);
private int cd;
private int timeout = 0;
private SocketServer server;
private Socket socket;
private Thread thread;
private List<Message> data;
private ResponseMonitor responseMonitor;
private EventListener eventListener;
private boolean closed;
private String replyto;
public SocketReceiverImpl(SocketServer server, Properties properties, int cd,
ResponseMonitor responseMonitor, EventListener eventListener) throws ConnectionException {
log.debug("create socket receiver with server");
this.server = server;
this.responseMonitor = responseMonitor;
this.eventListener = eventListener;
this.cd = cd;
this.replyto = new StringBuffer().append(server.getAddr()).append(":").append(server.getPort()).append(":").append(cd).toString();
timeout = Integer.parseInt(properties.getProperty("ReceiveTimeout")) * 1000
+ Integer.parseInt(properties.getProperty("TimeToLive")) * 1000;
log.debug("Timeout set as: " + timeout);
server.register(cd, responseMonitor, eventListener);
}
public SocketReceiverImpl(Socket socket, String replyto, Properties properties) {
log.debug("create socket receiver with socket: " + socket.getRemoteSocketAddress() + " " + socket.getLocalPort());
this.socket = socket;
this.replyto = replyto;
this.data = new ArrayList<Message>();
this.server = null;
this.responseMonitor = null;
this.eventListener = null;
this.cd = Integer.parseInt(replyto.split(":")[2]);
timeout = Integer.parseInt(properties.getProperty("ReceiveTimeout")) * 1000
+ Integer.parseInt(properties.getProperty("TimeToLive")) * 1000;
log.debug("Timeout set as: " + timeout);
this.thread = new Thread(this);
this.thread.setDaemon(true);
this.thread.start();
}
public int getCd() {
return cd;
}
public Message receive(long flags) throws ConnectionException {
if (closed) {
throw new ConnectionException(Connection.TPEPROTO, "Receiver already closed");
}
Message message = null;
if ((flags & Connection.TPNOBLOCK) != Connection.TPNOBLOCK) {
if(server != null) {
message = server.receiveMessage(cd, determineTimeout(flags));
} else if(socket != null) {
synchronized (this) {
if ((flags & Connection.TPNOBLOCK) != Connection.TPNOBLOCK) {
if(data.isEmpty()) {
try {
wait(determineTimeout(flags));
} catch (InterruptedException e) {
}
}
}
if(!data.isEmpty()) {
message = data.remove(0);
}
}
}
} else {
log.debug("Not waiting for the response, hope its there!");
}
if(message == null && (flags & Connection.TPNOBLOCK) == Connection.TPNOBLOCK) {
throw new ConnectionException(Connection.TPEBLOCK, "Did not receive a message");
} else if (message == null) {
if (JtsTransactionImple.hasTransaction()) {
try {
log.debug("Marking rollbackOnly");
TransactionImpl.current().rollback_only();
} catch (TransactionException e) {
throw new ConnectionException(Connection.TPESYSTEM, "Could not mark transaction for rollback only");
}
}
throw new ConnectionException(Connection.TPETIME, "Did not receive a message");
} else {
log.debug("Message was available");
if (message.rval == EventListener.DISCON_CODE) {
if (TransactionImpl.current() != null) {
try {
log.debug("Marking rollbackOnly as disconnection");
TransactionImpl.current().rollback_only();
} catch (TransactionException e) {
throw new ConnectionException(Connection.TPESYSTEM,
"Could not mark transaction for rollback only");
}
}
} else if (message.rcode == Connection.TPESVCERR) {
if (TransactionImpl.current() != null) {
try {
log.debug("Marking rollbackOnly as svc err");
TransactionImpl.current().rollback_only();
} catch (TransactionException e) {
throw new ConnectionException(Connection.TPESYSTEM,
"Could not mark transaction for rollback only");
}
}
} else if (message.rval == Connection.TPFAIL) {
if (TransactionImpl.current() != null) {
try {
TransactionImpl.current().rollback_only();
} catch (TransactionException e) {
throw new ConnectionException(Connection.TPESYSTEM,
"Could not mark transaction for rollback only");
}
}
}
}
if (responseMonitor != null) {
responseMonitor.responseReceived(cd, true);
}
return message;
}
public Object getReplyTo() throws ConnectionException {
return replyto;
}
public void close() throws ConnectionException {
if(server != null && cd != -1) {
server.unregister(cd);
}
if(thread != null) {
try {
thread.join();
} catch (InterruptedException e) {
log.warn("receiver close join socket thread failed with " + e);
}
}
if(socket != null) {
try {
socket.shutdownInput();
} catch (SocketException e) {
} catch (Exception e) {
log.warn("receiver shutdownInput failed with " + e);
}
}
closed = true;
}
public int determineTimeout(long flags) throws ConnectionException {
if ((flags & Connection.TPNOTIME) == Connection.TPNOTIME) {
return 0;
} else {
return timeout;
}
}
public void run() {
try {
DataInputStream ins = new DataInputStream(socket.getInputStream());
int size;
while((size = ins.readInt()) != -1) {
Message message = new Message();
byte[] buf = new byte[size];
int readn = 0;
int remain = size;
while(remain > 0) {
int n;
n = ins.read(buf, readn, remain);
if(n != -1) {
remain -= n;
readn += n;
} else {
log.error("expect " + size + " but read " + readn);
break;
}
}
if(remain == 0) {
log.debug("receive from " + socket + " and size is " + size + " buffer is " + buf);
String[] s = new String(buf).split("\n");
log.debug("sid is " + s[0]);
log.debug("cd is " + s[1]);
message.cd = Integer.parseInt(s[1]);
log.debug("rcode is " + s[2]);
message.rcode = Integer.parseInt(s[2]);
log.debug("len is " + s[3]);
message.len = Integer.parseInt(s[3]);
log.debug("flags is " + s[4]);
message.flags = Integer.parseInt(s[4]);
log.debug("rval is " + s[5]);
message.rval = Short.parseShort(s[5]);
log.debug("replyto is " + s[6]);
message.replyTo = s[6].equals("(null)") ? null : s[6];
log.debug("type is " + s[7]);
message.type = s[7].equals("(null)") ? null : s[7];
log.debug("subtype is " + s[8]);
message.subtype = s[8].equals("(null)") ? null : s[8];
message.data = new byte[message.len];
System.arraycopy(buf, size - message.len, message.data, 0, message.len);
log.debug("data is " + new String(message.data));
synchronized(this) {
if (eventListener != null) {
log.debug("Event listener will be called back");
if (message.rval == EventListener.DISCON_CODE) {
eventListener.setLastEvent(Connection.TPEV_DISCONIMM, message.rcode);
} else if (message.rcode == Connection.TPESVCERR) {
eventListener.setLastEvent(Connection.TPEV_SVCERR, message.rcode);
} else if (message.rval == Connection.TPFAIL) {
eventListener.setLastEvent(Connection.TPEV_SVCFAIL, message.rcode);
}
}
data.add(message);
if (responseMonitor != null) {
responseMonitor.responseReceived(this.cd, false);
}
notify();
}
}
}
} catch (EOFException e) {
log.info("receiver " + socket + " close");
closed = true;
} catch (SocketException e) {
} catch (IOException e) {
log.error("receiver " + socket + " run failed with " + e);
}
}
public Object getEndpoint() throws ConnectionException {
if(socket != null) {
return socket;
} else if(server != null && cd != -1){
return server.getClientSocket(cd);
} else return null;
}
}