Package org.jboss.blacktie.jatmibroker.xatmi

Source Code of org.jboss.blacktie.jatmibroker.xatmi.Connection$ResponseMonitorImpl

/*
* 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.blacktie.jatmibroker.xatmi;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jboss.blacktie.jatmibroker.core.ResponseMonitor;
import org.jboss.blacktie.jatmibroker.core.conf.ConfigurationException;
import org.jboss.blacktie.jatmibroker.core.transport.Message;
import org.jboss.blacktie.jatmibroker.core.transport.Receiver;
import org.jboss.blacktie.jatmibroker.core.transport.Transport;
import org.jboss.blacktie.jatmibroker.core.transport.TransportFactory;

/**
* This is the connection to remote BlackTie services. It must be created using
* the ConnectionFactory.
*
* @see ConnectionFactory#getConnection()
*/
public class Connection {

  /**
   * The logger to use.
   */
  private static final Logger log = LogManager.getLogger(Connection.class);

  // AVAILABLE FLAGS
  public static final int TPNOBLOCK = 0x00000001;
  public static final int TPSIGRSTRT = 0x00000002;
  public static final int TPNOREPLY = 0x00000004;
  public static final int TPNOTRAN = 0x00000008;
  public static final int TPTRAN = 0x00000010;
  public static final int TPNOTIME = 0x00000020;
  public static final int TPGETANY = 0x00000080;
  public static final int TPNOCHANGE = 0x00000100;
  public static final int TPCONV = 0x00000400;
  public static final int TPSENDONLY = 0x00000800;
  public static final int TPRECVONLY = 0x00001000;

  // ERROR CONDITIONS
  public static final int TPEBADDESC = 2;
  public static final int TPEBLOCK = 3;
  public static final int TPEINVAL = 4;
  public static final int TPELIMIT = 5;
  public static final int TPENOENT = 6;
  public static final int TPEOS = 7;
  public static final int TPEPROTO = 9;
  public static final int TPESVCERR = 10;
  public static final int TPESVCFAIL = 11;
  public static final int TPESYSTEM = 12;
  public static final int TPETIME = 13;
  public static final int TPETRAN = 14;
  public static final int TPGOTSIG = 15;
  public static final int TPEITYPE = 17;
  public static final int TPEOTYPE = 18;
  public static final int TPEEVENT = 22;
  public static final int TPEMATCH = 23;

  // SERVICE CONDITIONS
  public static final short TPFAIL = 0x00000001;
  public static final short TPSUCCESS = 0x00000002;

  // Events
  public static final long TPEV_DISCONIMM = 0x0001;
  public static final long TPEV_SVCERR = 0x0002;
  public static final long TPEV_SVCFAIL = 0x0004;
  public static final long TPEV_SVCSUCC = 0x0008;
  public static final long TPEV_SENDONLY = 0x0020;

  public static final int XATMI_SERVICE_NAME_LENGTH = 15;

  /**
   * The next id to use for session connection descriptors.
   */
  private static int nextId;

  /**
   * The transports created for each service.
   */
  private Map<String, Transport> transports = new HashMap<String, Transport>();

  /**
   * Any local temporary queues created in this connection
   */
  private Map<java.lang.Integer, Receiver> temporaryQueues = new HashMap<java.lang.Integer, Receiver>();

  /**
   * The properties that this connection was created with.
   */
  Properties properties;

  /**
   * The open sessions from this connection/
   */
  private Map<Integer, Session> sessions = new HashMap<Integer, Session>();

  /**
   * The list of sessionids that have received a message.
   */
  private List<Integer> tpGetAnySessions = new ArrayList<Integer>();

  /**
   * The monitor of responses.
   */
  private ResponseMonitor responseMonitor;

  /**
   * The connection factory that created this connection.
   */
  private ConnectionFactory connectionFactory;

  /**
   * The service sessions for this connection.
   */
  private Session serviceSession;

  /**
   * The connection
   *
   * @param connectionFactory
   *            The connection factory that created this connection.
   * @param properties
   *            The properties that this connection was created with.
   */
  Connection(ConnectionFactory connectionFactory, Properties properties) {
    this.connectionFactory = connectionFactory;
    this.properties = properties;
    responseMonitor = new ResponseMonitorImpl();
  }

  /**
   * Allocate a new buffer
   *
   * @param type
   *            The type of the buffer
   * @param subtype
   *            The subtype of the buffer
   * @return The new buffer
   * @throws ConnectionException
   *             If the buffer was unknown or invalid.
   */
  public Buffer tpalloc(String type, String subtype, int len)
      throws ConnectionException {
    if (type == null) {
      throw new ConnectionException(Connection.TPEINVAL,
          "No type provided");
    } else if (type.equals("X_OCTET")) {
      log.debug("Initializing a new X_OCTET");
      return new X_OCTET(len);
    } else if (type.equals("X_C_TYPE")) {
      log.debug("Initializing a new X_C_TYPE");
      return new X_C_TYPE(subtype, properties);
    } else if (type.equals("X_COMMON")) {
      log.debug("Initializing a new X_COMMON");
      return new X_COMMON(subtype, properties);
    } else {
      throw new ConnectionException(Connection.TPENOENT,
          "Type was not known: " + type);
    }
  }

  /**
   * Synchronous call.
   *
   * @param svc
   *            The name of the service to call
   * @param buffer
   *            The inbound data
   * @param len
   *            The length of the data
   * @param flags
   *            The flags to use
   * @return The returned buffer
   * @throws ConnectionException
   *             If the service cannot be contacted.
   */
  public Response tpcall(String svc, Buffer buffer, int flags)
      throws ConnectionException {
    log.debug("tpcall");
    int tpacallFlags = flags;
    tpacallFlags &= ~TPNOCHANGE;
    int cd = tpacall(svc, buffer, tpacallFlags);
    return receive(cd, flags);
  }

  /**
   * Asynchronous call
   *
   * @param svc
   *            The name of the service to call
   * @param toSend
   *            The inbound data
   * @param len
   *            The length of the data
   * @param flags
   *            The flags to use
   * @return The connection descriptor
   * @throws ConnectionException
   *             If the service cannot be contacted.
   */
  public int tpacall(String svc, Buffer toSend, int flags)
      throws ConnectionException {
    log.debug("tpacall");
    int toCheck = flags
        & ~(TPNOTRAN | TPNOREPLY | TPNOBLOCK | TPNOTIME | TPSIGRSTRT);
    if (toCheck != 0) {
      log.trace("invalid flags remain: " + toCheck);
      throw new ConnectionException(Connection.TPEINVAL,
          "Invalid flags remain: " + toCheck);
    }

    svc = svc.substring(0,
        Math.min(Connection.XATMI_SERVICE_NAME_LENGTH, svc.length()));
    int correlationId = 0;
    synchronized (this) {
      correlationId = ++nextId;
      log.trace("Allocated next sessionId: " + correlationId);
    }
    Transport transport = getTransport(svc);
    Receiver endpoint = transport.createReceiver(correlationId,
        responseMonitor);
    temporaryQueues.put(correlationId, endpoint);
    log.trace("Added a queue for: " + correlationId);
    // TODO HANDLE TRANSACTION
    String type = null;
    String subtype = null;
    int len = 0;
    byte[] data = null;
    if (toSend != null) {
      data = toSend.serialize();
      type = toSend.getType();
      subtype = toSend.getSubtype();
      len = toSend.getLen();
    }

    String timeToLive = properties.getProperty("TimeToLive");
    int ttl = 0;

    if (timeToLive != null) {
      ttl = Integer.parseInt(timeToLive) * 1000;
    }
    transport.getSender(svc, false).send(endpoint.getReplyTo(), (short) 0,
        0, data, len, correlationId, flags, ttl, type, subtype);
    if ((flags & Connection.TPNOREPLY) == Connection.TPNOREPLY) {
      correlationId = 0;
    }
    log.debug("Returning cd: " + correlationId);
    return correlationId;
  }

  /**
   * Cancel the outstanding asynchronous call.
   *
   * @param cd
   *            The connection descriptor
   * @throws ConnectionException
   *             If the client cannot be cleaned up.
   */
  public int tpcancel(int cd) throws ConnectionException {
    log.debug("tpcancel: " + cd);
    int toReturn = -1;
    Receiver endpoint = temporaryQueues.remove(cd);
    if (endpoint != null) {
      log.debug("closing endpoint");
      endpoint.close();
      log.debug("endpoint closed");
      toReturn = 0;
    } else {
      log.debug("No endpoint available");
      throw new ConnectionException(Connection.TPEBADDESC, "cd " + cd
          + " does not exist");
    }
    log.debug("tpcancel returning: " + toReturn);
    return toReturn;
  }

  /**
   * Get the reply for an asynchronous call.
   *
   * @param cd
   *            The connection descriptor to use
   * @param flags
   *            The flags to use
   * @return The response from the server
   * @throws ConnectionException
   *             If the service cannot be contacted.
   */
  public Response tpgetrply(int cd, int flags) throws ConnectionException {
    log.debug("tpgetrply: " + cd);
    int toCheck = flags
        & ~(TPGETANY | TPNOCHANGE | TPNOBLOCK | TPNOTIME | TPSIGRSTRT);
    if (toCheck != 0) {
      log.trace("invalid flags remain: " + toCheck);
      throw new ConnectionException(Connection.TPEINVAL,
          "Invalid flags remain: " + toCheck);
    }

    synchronized (tpGetAnySessions) {
      if ((flags & Connection.TPGETANY) == Connection.TPGETANY) {
        if ((flags & Connection.TPNOBLOCK) != Connection.TPNOBLOCK) {
          int timeout = 0;
          if ((flags & Connection.TPNOTIME) != Connection.TPNOTIME) {
            timeout = Integer.parseInt(properties
                .getProperty("RequestTimeout"))
                * 1000
                + Integer.parseInt(properties
                    .getProperty("TimeToLive")) * 1000;
          }
          if (tpGetAnySessions.size() == 0) {
            try {
              tpGetAnySessions.wait(timeout);
            } catch (InterruptedException e) {
              throw new ConnectionException(Connection.TPESYSTEM,
                  "Could not wait", e);
            }
          }
          if (tpGetAnySessions.size() == 0) {
            throw new ConnectionException(Connection.TPETIME,
                "No message arrived");
          }
        } else if (tpGetAnySessions.size() == 0) {
          throw new ConnectionException(Connection.TPEBLOCK,
              "No message arrived");
        }
        cd = tpGetAnySessions.remove(0);
      }
    }

    Response toReturn = receive(cd, flags);
    log.debug("tpgetrply returning: " + toReturn);
    return toReturn;
  }

  /**
   * Handle the initiation of a conversation with the server.
   *
   * @param svc
   *            The name of the service
   * @param toSend
   *            The outbound buffer
   * @param len
   *            The length of the data
   * @param flags
   *            The flags to use
   * @return The connection descriptor
   * @throws ConnectionException
   *             If the service cannot be contacted.
   */
  public Session tpconnect(String svc, Buffer toSend, int flags)
      throws ConnectionException {
    log.debug("tpconnect: " + svc);

    svc = svc.substring(0,
        Math.min(Connection.XATMI_SERVICE_NAME_LENGTH, svc.length()));
    // Initiate the session
    svc = svc.substring(0,
        Math.min(Connection.XATMI_SERVICE_NAME_LENGTH, svc.length()));
    int correlationId = 0;
    synchronized (this) {
      correlationId = nextId++;
    }
    Transport transport = getTransport(svc);
    Session session = new Session(this, svc, transport, correlationId);

    Receiver receiver = session.getReceiver();
    // TODO HANDLE TRANSACTION
    String type = null;
    String subtype = null;
    int len = 0;
    byte[] data = null;
    if (toSend != null) {
      data = toSend.serialize();
      type = toSend.getType();
      subtype = toSend.getSubtype();
      len = toSend.getLen();
    }

    String timeToLive = properties.getProperty("TimeToLive");
    int ttl = 0;

    if (timeToLive != null) {
      ttl = Integer.parseInt(timeToLive) * 1000;
    }
    log.debug("tpconnect sending data");
    session.getSender().send(receiver.getReplyTo(), (short) 0, 0, data,
        len, correlationId, flags | TPCONV, ttl, type, subtype);

    byte[] response = null;
    try {
      log.debug("tpconnect receiving data");
      X_OCTET odata = (X_OCTET) session.tprecv(0);
      // TODO THIS SHOULD BE A BETTER ERROR AND CHECKED IF TPCONV AND NOT
      // CONV
      response = odata.getByteArray();
      log.debug("tpconnect received data");
    } catch (ResponseException e) {
      response = ((X_OCTET) e.getReceived()).getByteArray();
      log.debug("Caught an exception with data", e);
    } catch (ConnectionException e) {
      session.close();
      throw new ConnectionException(e.getTperrno(), "Could not connect");
    }
    byte[] ack = new byte[4];
    byte[] bytes = "ACK".getBytes();
    System.arraycopy(bytes, 0, ack, 0, 3);
    boolean connected = response == null ? false : Arrays.equals(ack,
        response);
    if (!connected) {
      log.error("Could not connect");
      session.close();
      throw new ConnectionException(Connection.TPESYSTEM,
          "Could not connect");
    }
    session.setCreatorState(flags);
    sessions.put(correlationId, session);
    log.trace("Added session: " + correlationId);

    // Return a handle to allow the connection to send/receive data on
    log.debug("Created session: " + correlationId);
    return session;
  }

  /**
   * Close any resources associated with this connection
   *
   * @throws ConnectionException
   *             If an open session cannot be cancelled or disconnected.
   */
  public void close() throws ConnectionException {
    log.debug("Close connection called");

    // MUST close the session first to remove the temporary queue
    Session[] sessions = new Session[this.sessions.size()];
    sessions = this.sessions.values().toArray(sessions);
    for (int i = 0; i < sessions.length; i++) {
      log.debug("closing session: " + sessions[i].getCd());
      sessions[i].tpdiscon();
      log.debug("Closed open session: " + sessions[i].getCd());
    }
    this.sessions.clear();
    log.trace("Removed all sessions");

    Receiver[] receivers = new Receiver[temporaryQueues.size()];
    receivers = temporaryQueues.values().toArray(receivers);
    for (int i = 0; i < receivers.length; i++) {
      log.debug("closing receiver");
      tpcancel(receivers[i].getCd());
      log.warn("Closed open receiver");
    }
    temporaryQueues.clear();
    log.trace("Temporary queues cleared");

    if (serviceSession != null) {
      log.debug("closing service session");
      serviceSession.close();
      serviceSession = null;
      log.debug("Closed open service session");
    }

    Iterator<Transport> transports = this.transports.values().iterator();
    while (transports.hasNext()) {
      Transport transport = transports.next();
      log.debug("closing transport");
      transport.close();
      log.debug("closed transport");
    }
    this.transports.clear();
    this.connectionFactory.removeConnection(this);
    log.debug("Close connection finished");
  }

  private Transport getTransport(String serviceName)
      throws ConnectionException {
    Transport toReturn = transports.get(serviceName);
    if (toReturn == null) {
      try {
        toReturn = TransportFactory.getTransportFactory(properties)
            .createTransport();
      } catch (ConfigurationException e) {
        throw new ConnectionException(Connection.TPENOENT,
            "Could not load transport for: " + serviceName, e);
      }
      transports.put(serviceName, toReturn);
    }
    return toReturn;
  }

  /**
   * Retrieve a response.
   *
   * @param cd
   *            The connection descriptor
   * @param flags
   *            The flags to use
   * @return The response
   * @throws ConnectionException
   *             If the response cannot be retrieved.
   */
  private Response receive(int cd, int flags) throws ConnectionException {
    log.debug("receive: " + cd);
    Receiver endpoint = temporaryQueues.get(cd);
    if (endpoint == null) {
      throw new ConnectionException(Connection.TPEBADDESC,
          "Session does not exist: " + cd);
    }
    Message message = endpoint.receive(flags);
    temporaryQueues.remove(cd);
    Buffer buffer = null;
    if (message.type != null && !message.type.equals("")) {
      buffer = tpalloc(message.type, message.subtype, message.len);
      buffer.deserialize(message.data);
    }
    if (message.rval == Connection.TPFAIL) {
      if (message.rcode == Connection.TPESVCERR) {
        throw new ResponseException(Connection.TPESVCERR,
            "Got an error back from the remote service", -1,
            message.rcode, buffer);
      }
      throw new ResponseException(Connection.TPESVCFAIL,
          "Got a fail back from the remote service", -1,
          message.rcode, buffer);
    } else {
      Response response = new Response(cd, message.rval, message.rcode,
          buffer, message.flags);

      log.debug("received returned a response? "
          + (response == null ? "false" : "true"));
      return response;
    }
  }

  /**
   * Used by the service side to create a session for handling the client
   * request.
   *
   * @param name
   *            The name of the service.
   * @param cd
   *            The connection descriptor
   * @param replyTo
   *            The client to respond to
   * @return The session to use for the service invocation
   * @throws ConnectionException
   *             In case the transport cannot be established.
   */
  Session createServiceSession(String name, int cd, Object replyTo)
      throws ConnectionException {
    log.trace("Creating the service session");
    if (serviceSession != null) {
      throw new ConnectionException(Connection.TPEPROTO,
          "Second service session creation attempt, was: "
              + serviceSession.getCd() + " new: " + cd);
    }
    Transport transport = getTransport(name);
    serviceSession = new Session(this, transport, cd, replyTo);
    log.trace("Created the service session: " + cd);
    return serviceSession;
  }

  /**
   * Does this connection have any open sessions? Used to determine if a
   * service has unanswered requests prior to a tpreturn.
   *
   * @return True, if there are open conversations or asynchronous XATMI calls
   *         open.
   */
  boolean hasOpenSessions() {
    return sessions.size() > 0 || temporaryQueues.size() > 0;
  }

  /**
   * Detach the open session, called during {@link Session#close()}
   *
   * @param session
   *            The session that is closing.
   */
  void removeSession(Session session) {
    log.debug("Removing session: " + session.getCd());
    // May be a no-op
    boolean remove = false;
    Iterator<Integer> iterator = sessions.keySet().iterator();
    while (iterator.hasNext()) {
      Integer next = iterator.next();
      if (next.intValue() == session.getCd()) {
        iterator.remove();
        log.trace("Removed session: " + session.getCd());
        remove = true;
        break;
      } else {
        log.trace("Ignoring: " + next);
      }
    }
    if (!remove) {
      log.debug("Session did not exist: " + session.getCd() + " size: "
          + sessions.size());
    }

    if (session.equals(serviceSession)) {
      serviceSession = null;
    }
    log.debug("Removed session: " + session.getCd());
  }

  /**
   * This class allows the session to notify the connection when a response is
   * delivered or consumed.
   */
  private class ResponseMonitorImpl implements ResponseMonitor {
    /**
     * Handle the response update.
     */
    public void responseReceived(int sessionId, boolean remove) {
      synchronized (tpGetAnySessions) {
        if (!remove) {
          log.trace("tpgetanyCallback adding: " + sessionId);
          tpGetAnySessions.add(sessionId);
          tpGetAnySessions.notify();
        } else {
          log.trace("tpgetanyCallback removing: " + sessionId);
          for (int i = 0; i < tpGetAnySessions.size(); i++) {
            if (sessionId == tpGetAnySessions.get(i)) {
              tpGetAnySessions.remove(i);
              log.trace("tpgetanyCallback removed: " + sessionId);
              break;
            }
          }
        }
      }
    }
  }
}
TOP

Related Classes of org.jboss.blacktie.jatmibroker.xatmi.Connection$ResponseMonitorImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.