/* * 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.xatmi.impl; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.jboss.narayana.blacktie.jatmibroker.core.conf.ConfigurationException; import org.jboss.narayana.blacktie.jatmibroker.core.transport.EventListener; 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.transport.Sender; import org.jboss.narayana.blacktie.jatmibroker.core.transport.Transport; 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.Buffer; import org.jboss.narayana.blacktie.jatmibroker.xatmi.ConnectionException; import org.jboss.narayana.blacktie.jatmibroker.xatmi.ResponseException; import org.jboss.narayana.blacktie.jatmibroker.xatmi.Session; /** * A session reference may either be obtained from the tpconnect * <code>Connection</code> invocation for a client or retrieved from the * TPSVCINFO structure for a service (assuming the service was invoked within * the scope of a tpconnect). * * It is used to send and retrieve data: * ConnectionImpl#tpconnect(String, BufferImpl, int, int) * TPSVCINFO_Impl#getSession() */ public class SessionImpl implements Session { /** * A logger to use. */ private static final Logger log = LogManager.getLogger(SessionImpl.class); /** * The transport to manage data on */ private Transport transport; /** * The descriptor */ private int cd; /** * The sessions sender */ private Sender sender; /** * The sessions receiver */ private Receiver receiver; /** * The event listener allows us to hear events on tpsend */ private EventListener eventListener; /** * The last event received by the session, this is either discon, SUCC, * FAIL, ERR */ private long lastEvent = -1; /** * The last rcode */ private int lastRCode = 0; /** * Is the session in read mode. */ private boolean canSend = true; /** * Is the session in write mode, so to speak. */ private boolean canRecv = true; /** * The connection to use. */ private ConnectionImpl connection; private boolean closed; /** * Create a new session during tpconnect. * * @param connection * The connection that created the session * @param transport * The transport to create actors on. * @param cd * The cd of the session * * @throws ConnectionException * In case the receiver cannot be created. * @see ConnectionImpl#tpconnect(String, BufferImpl, int, int) */ SessionImpl(ConnectionImpl connection, String serviceName, Transport transport, int cd) throws ConnectionException { log.debug("Creating a new client session: " + cd); this.connection = connection; this.transport = transport; this.cd = cd; this.eventListener = new EventListenerImpl(this); this.receiver = transport.createReceiver(cd, null, eventListener); this.sender = transport.getSender(serviceName, true); this.canSend = false; this.canRecv = true; } /** * Create a service side session for a pre-established client connection. * * @param connection * The connection to use. * @param transport * The transport to use. * @param cd * The connection descriptor to use. * @param replyTo * The client to reply to. * @throws ConnectionException * In case the receiver or sender cannot be established. * @see ConnectionImpl#createServiceSession(String, int, Object) */ SessionImpl(ConnectionImpl connection, Transport transport, int cd, Object replyTo) throws ConnectionException { log.debug("Connecting a client session for the service: " + cd); this.connection = connection; this.transport = transport; this.cd = cd; this.eventListener = new EventListenerImpl(this); if (replyTo != null && !replyTo.equals("")) { this.sender = transport.createSender(replyTo); } else { log.trace("NO REPLY TO REQUIRED"); } this.receiver = transport.createReceiver(this.sender); this.canRecv = false; this.canSend = true; } /** * Client side initialization during tpconnect. * * @param flags * The flags */ void setCreatorState(long flags) { // Sort out session state if ((flags & ConnectionImpl.TPSENDONLY) == ConnectionImpl.TPSENDONLY) { canSend = true; canRecv = false; } else if ((flags & ConnectionImpl.TPRECVONLY) == ConnectionImpl.TPRECVONLY) { canSend = false; canRecv = true; } } /** * Set the state of the session using the flags. This is so we can respond * from the service easily with the <code>ACK</code> that the client expects * in initialization of the connection. * * @param flags */ public void setCreatedState(long flags) { // Sort out session state if ((flags & ConnectionImpl.TPSENDONLY) == ConnectionImpl.TPSENDONLY) { canSend = false; canRecv = true; } else if ((flags & ConnectionImpl.TPRECVONLY) == ConnectionImpl.TPRECVONLY) { canSend = true; canRecv = false; } } /** * Close the session * * @throws ConnectionException */ public void close() throws ConnectionException { log.debug("Closing session: " + cd); if (closed) { throw new ConnectionException(ConnectionImpl.TPEPROTO, "Session already closed"); } if (sender != null) { log.debug("Sender closing"); sender.close(); sender = null; } if (receiver != null) { log.debug("Receiver closing"); receiver.close(); receiver = null; } connection.removeSession(this); closed = true; log.debug("Closed session: " + cd); } /** * Send a buffer to a remote server in a conversation * * @param toSend * The outbound data * @param flags * The flags to use * @throws ConnectionException * If the message cannot be sent. */ public int tpsend(Buffer toSend, int flags) throws ConnectionException { log.debug("tpsend invoked: " + cd); if (closed) { throw new ConnectionException(ConnectionImpl.TPEPROTO, "Session already closed"); } int toReturn = -1; int toCheck = flags & ~(ConnectionImpl.TPRECVONLY | ConnectionImpl.TPNOBLOCK | ConnectionImpl.TPNOTIME | ConnectionImpl.TPSIGRSTRT); if (toCheck != 0) { log.trace("invalid flags remain: " + toCheck); throw new ConnectionException(ConnectionImpl.TPEINVAL, "Invalid flags remain: " + toCheck); } if (this.lastEvent > -1) { throw new ResponseException(ConnectionImpl.TPEEVENT, "Event existed on descriptor: " + lastEvent, lastEvent, lastRCode, null); } else if (!canSend) { throw new ConnectionException(ConnectionImpl.TPEPROTO, "Session can't send"); } // Can only send in certain circumstances if (sender != null) { log.debug("Sender not null, sending"); String type = null; String subtype = null; byte[] data = null; int len = 0; if (toSend != null) { data = ((BufferImpl) toSend).serialize(); type = toSend.getType(); subtype = toSend.getSubtype(); len = toSend.getLen(); } sender.send(receiver.getReplyTo(), (short) 0, 0, data, len, cd, flags, 0, type, subtype); // Sort out session state if ((flags & ConnectionImpl.TPRECVONLY) == ConnectionImpl.TPRECVONLY) { canSend = false; canRecv = true; } toReturn = 0; } else { throw new ConnectionException(ConnectionImpl.TPEPROTO, "Session in receive mode"); } return toReturn; } /** * Received the next response in a conversation * * @param flags * The flags to use * @return The next response * @throws ConnectionException * If the message cannot be received or the flags are incorrect * @throws ConfigurationException */ public Buffer tprecv(int flags) throws ConnectionException, ConfigurationException { log.debug("Receiving: " + cd); if (closed) { throw new ConnectionException(ConnectionImpl.TPEPROTO, "Session already closed"); } int toCheck = flags & ~(ConnectionImpl.TPNOCHANGE | ConnectionImpl.TPNOBLOCK | ConnectionImpl.TPNOTIME | ConnectionImpl.TPSIGRSTRT); if (toCheck != 0) { log.trace("invalid flags remain: " + toCheck); throw new ConnectionException(ConnectionImpl.TPEINVAL, "Invalid flags remain: " + toCheck); } if (!canRecv) { throw new ConnectionException(ConnectionImpl.TPEPROTO, "Session can't receive"); } Message m = receiver.receive(flags); // Prepare the outbound channel if (m.replyTo == null || (sender != null && !m.replyTo.equals(sender.getSendTo()))) { log.trace("Send to location has altered"); sender.close(); sender = null; } if (sender == null && m.replyTo != null && !m.replyTo.equals("")) { log.trace("Will require a new sender"); if(((String)m.replyTo).contains("IOR:")) { sender = transport.createSender(m.replyTo); } else { sender = transport.createSender(receiver); } } else { log.debug("Not setting the sender"); } BufferImpl received = null; if (m.type != null && !m.type.equals("")) { received = (BufferImpl) connection .tpalloc(m.type, m.subtype); received.deserialize(m.data); } log.debug("Prepared and ready to launch"); // Sort out session state if ((m.flags & ConnectionImpl.TPRECVONLY) == ConnectionImpl.TPRECVONLY) { canSend = true; canRecv = false; } // Check the condition of the response if ((m.flags & ConnectionImpl.TPRECVONLY) == ConnectionImpl.TPRECVONLY) { throw new ResponseException(ConnectionImpl.TPEEVENT, "Reporting send only event", ConnectionImpl.TPEV_SENDONLY, m.rcode, received); } else if (m.rval == EventListener.DISCON_CODE) { close(); throw new ResponseException(ConnectionImpl.TPEEVENT, "Received a disconnect event", ConnectionImpl.TPEV_DISCONIMM, m.rcode, received); } else if (m.rval == ConnectionImpl.TPSUCCESS || m.rval == ConnectionImpl.TPFAIL) { log.debug("Completed session is being closed: " + cd); close(); if (m.rval == ConnectionImpl.TPSUCCESS) { throw new ResponseException(ConnectionImpl.TPEEVENT, "Service completed successfully event", ConnectionImpl.TPEV_SVCSUCC, 0, received); } else if (m.rcode == ConnectionImpl.TPESVCERR) { throw new ResponseException(ConnectionImpl.TPEEVENT, "Service received an error", ConnectionImpl.TPEV_SVCERR, m.rcode, received); } else { throw new ResponseException(ConnectionImpl.TPEEVENT, "Service received a fail", ConnectionImpl.TPEV_SVCFAIL, m.rcode, received); } } return received; } /** * Close the conversation with the remote service. This will close the * session. */ public void tpdiscon() throws ConnectionException { log.debug("tpdiscon: " + cd); if (closed) { throw new ConnectionException(ConnectionImpl.TPEPROTO, "Session already closed"); } if (sender == null) { throw new ConnectionException(ConnectionImpl.TPEPROTO, "Session had no endpoint to respond to for tpdiscon"); } if (TransactionImpl.current() != null) { try { TransactionImpl.current().rollback_only(); } catch (TransactionException e) { throw new ConnectionException(ConnectionImpl.TPESYSTEM, "Could not mark transaction for rollback only"); } } try { sender.send("", EventListener.DISCON_CODE, 0, null, 0, cd, 0, 0, null, null); } catch (org.omg.CORBA.OBJECT_NOT_EXIST one) { log.warn("The disconnect called failed to notify the remote end"); log.debug("The disconnect called failed to notify the remote end", one); } close(); } /** * Return the connection descriptor * * @return The connection descriptor id. */ int getCd() { return cd; } /** * Get the receiver on this session. * * @return The receiver */ Receiver getReceiver() { return receiver; } /** * Get the sessions sender. * * @return The sender */ public Sender getSender() { return sender; } /** * Set the last event seen on this session. * * @param lastEvent * The last event * @param rcode * The last rcode */ private void setLastEvent(long lastEvent, int rcode) { log.debug("Set lastEvent: " + lastEvent + "lastRCode: " + lastRCode + " cd: " + cd); this.lastEvent = lastEvent; this.lastRCode = rcode; } /** * A listener for events. */ private class EventListenerImpl implements EventListener { /** * The session to return events to. */ private SessionImpl session; /** * Create a new listener with a session to set events on. * * @param session * The session. */ public EventListenerImpl(SessionImpl session) { this.session = session; } /** * Pass the last event through to the session. */ public void setLastEvent(long lastEvent, int rcode) { session.setLastEvent(lastEvent, rcode); } } Transport getTransport() { return transport; } }