Package com.sun.sgs.impl.service.data.store.net

Source Code of com.sun.sgs.impl.service.data.store.net.DataStoreServerImpl$SocketExporter

/*
* Copyright 2007-2009 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.sun.sgs.impl.service.data.store.net;

import com.sun.sgs.app.TransactionNotActiveException;
import com.sun.sgs.app.TransactionTimeoutException;
import com.sun.sgs.impl.kernel.NullAccessCoordinator;
import com.sun.sgs.impl.service.data.store.BindingValue;
import com.sun.sgs.impl.service.data.store.DataStoreException;
import com.sun.sgs.impl.service.data.store.DataStoreImpl;
import com.sun.sgs.impl.sharedutil.LoggerWrapper;
import static com.sun.sgs.impl.sharedutil.Objects.checkNull;
import com.sun.sgs.impl.sharedutil.PropertiesWrapper;
import com.sun.sgs.impl.util.Exporter;
import com.sun.sgs.service.Transaction;
import com.sun.sgs.service.TransactionListener;
import com.sun.sgs.service.TransactionParticipant;
import com.sun.sgs.service.store.ClassInfoNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.NavigableMap;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import static java.util.logging.Level.FINEST;
import java.util.logging.Logger;

/**
* Provides an implementation of {@code DataStoreServer}, using the {@link
* DataStoreImpl} class to support the same database format which that class
* supports. <p>
*
* In addition to those supported by the {@link DataStoreImpl} class, the
* {@link #DataStoreServerImpl constructor} supports the following properties:
* <p>
*
* <dl style="margin-left: 1em">
*
* <dt> <i>Property:</i> <code><b>
*  com.sun.sgs.impl.service.data.store.net.max.txn.timeout
</b></code><br>
*      <i>Default:</i> {@code 600000}
*
* <dd style="padding-top: .5em">The maximum amount of time in milliseconds
*  that a transaction will be permitted to run before it is a candidate
*  for being aborted. <p>
*
* <dt> <i>Property:</i> <code><b>
*  com.sun.sgs.impl.service.data.store.net.server.reap.delay
</b></code><br>
*      <i>Default:</i> {@code 500}
*
* <dd style="padding-top: .5em">The delay in milliseconds between attempts to
*  reap timed out transactions. <p>
*
* <dt> <i>Property:</i> <code><b>
*  com.sun.sgs.impl.service.data.store.net.server.port
</b></code><br>
*      <i>Default:</i> {@code 44530}
*
* <dd style="padding-top: .5em">The network port for running the server. This
*  value must be greater than or equal to {@code 0} and no greater than
*  {@code 65535}.  If the value specified is {@code 0}, then an anonymous
*  port will be chosen.  The value chosen will be logged, and can also be
*  accessed with the {@link #getPort getPort} method. <p>
*
* </dl> <p>
*
* In addition to any logging performed by the {@code DataStoreImpl} class,
* this class uses the {@link Logger} named {@code
* com.sun.sgs.impl.service.data.store.net.server} to log
* information at the following levels: <p>
*
* <ul>
* <li> {@link Level#INFO INFO} - actual port if anonymous port was requested
* <li> {@link Level#CONFIG CONFIG} - server properties
* <li> {@link Level#FINE FINE} - allocation transaction IDs, problems
*  unexporting the server, reaping expired transactions, problems
*  the specified transaction ID
* <li> {@link Level#FINER FINER} - create transactions
* </ul> <p>
*/
public class DataStoreServerImpl implements DataStoreServer {

    /** The package for this class. */
    private static final String PACKAGE =
  "com.sun.sgs.impl.service.data.store.net";

    /** The logger for this class. */
    static final LoggerWrapper logger =
  new LoggerWrapper(Logger.getLogger(PACKAGE + ".server"));

    /** The property that specifies the maximum transaction timeout. */
    private static final String MAX_TXN_TIMEOUT_PROPERTY =
  PACKAGE + ".max.txn.timeout";

    /** The default maximum transaction timeout in milliseconds. */
    private static final long DEFAULT_MAX_TXN_TIMEOUT = 600000;

    /**
     * The property that specifies the delay in milliseconds between attempts
     * to reap timed out transactions.
     */
    private static final String REAP_DELAY_PROPERTY = PACKAGE +
  ".server.reap.delay";

    /** The default reap delay. */
    private static final long DEFAULT_REAP_DELAY = 500;

    /**
     * The name of the property for specifying the port for running the server.
     */
    private static final String PORT_PROPERTY = PACKAGE + ".server.port";

    /** The default value of the port for running the server. */
    private static final int DEFAULT_PORT = 44530;

    /** The number of transactions to allocate at a time. */
    private static final int TXN_ALLOCATION_BLOCK_SIZE = 100;

    /**
     * Whether to replace Java(TM) RMI with an experimental, socket-based
     * facility.
     */
    private static final boolean noRmi = Boolean.getBoolean(
  PACKAGE + ".no.rmi");

    /** The underlying data store. */
    private final CustomDataStoreImpl store;

    /** The maximum transaction timeout in milliseconds. */
    private final long maxTxnTimeout;

    /** The object used to export the server. */
    private final Exporter<DataStoreServer> exporter;

    /** The port for running the server. */
    private final int port;

    /** Used to execute the expired transaction reaper. */
    private final ScheduledExecutorService executor;

    /** Stores information about transactions. */
    TxnTable<?> txnTable;

    /** Implement Transactions using a long for the transaction ID. */
    private static class Txn implements Transaction {

  /**
   * The state value for when the transaction is not in use, prepared, or
   * being reaped.
   */
  private static final int IDLE = 0;

  /**
   * The state value for when the transaction is currently in use, and is
   * not prepared or being reaped.
   */
  private static final int IN_USE = 1;

  /**
   * The state value for when the transaction is not in use, has been
   * prepared, and is not being reaped.
   */
  private static final int PREPARED = 2;

  /**
   * The state value for when the transaction is currently in use and
   * prepared, and is not being reaped.
   */
  private static final int IN_USE_PREPARED = IN_USE | PREPARED;

  /**
   * The state value for when the transaction is being reaped because it
   * is expired.  Once this state is reached, it never changes.
   * Transactions that are in use or prepared are not reaped.
   */
  private static final int REAPING = 4;

  /** The transaction ID. */
  private final long tid;

  /** The creation time. */
  private final long creationTime;

  /** The timeout value. */
  private final long timeout;

  /** The information associated with this transaction, or null. */
  private Object txnInfo;

  /**
   * The current state, one of IDLE, IN_USE, PREPARED, IN_USE_PREPARED,
   * or REAPING.
   */
  private final AtomicInteger state = new AtomicInteger(IDLE);

  /** The transaction participant or null. */
  private TransactionParticipant participant;

  /** Whether the transaction has already started aborting. */
  private boolean aborting;

  /** Whether the transaction has been committed or aborted. */
  private boolean inactive;

  /**
   * The exception that caused the transaction to be aborted, or null if
   * no cause was provided or if no abort occurred.
   */
  private Throwable abortCause = null;

  /** Creates an instance with the specified ID and timeout. */
  Txn(long tid, long timeout) {
      this.tid = tid;
      this.timeout = timeout;
      creationTime = System.currentTimeMillis();
  }

  /** Returns the associated ID as a long. */
  long getTid() {
      return tid;
  }

  /**
   * Returns the information associated with this transaction, or
   * null.
   */
  Object getTxnInfo() {
      return txnInfo;
  }

  /** Sets the information associated with this transaction. */
  void setTxnInfo(Object txnInfo) {
      this.txnInfo = txnInfo;
  }

  /**
   * Sets whether this transaction is in use, doing nothing if the state
   * is REAPING.  Returns whether the attempt to set the state was
   * successful.  The attempt succeeds if the state is REAPING or if the
   * IN_USE bit is the opposite of the requested state, independent of
   * the PREPARED bit.
   */
  boolean setInUse(boolean inUse) {
      int prepared = state.get() & PREPARED;
      int expect = (inUse ? IDLE : IN_USE) | prepared;
      int update = (inUse ? IN_USE : IDLE) | prepared;
      return state.compareAndSet(expect, update) ||
    state.get() == REAPING;
  }

  /**
   * Sets this transaction as being reaped.  Returns whether the attempt
   * to set the state was successful.  The attempt fails if the
   * transaction is in use or if it has been prepared.
   */
  boolean setReaping() {
      boolean success = state.compareAndSet(IDLE, REAPING);
      return success || state.get() == REAPING;
  }

  /** Returns true if this transaction is being reaped. */
  boolean getReaping() {
      return state.get() == REAPING;
  }

  /**
   * Marks the transaction as prepared.  This method should only be
   * called when the transaction is in use and has not already been
   * prepared.
   */
  void setPrepared() {
      boolean success = state.compareAndSet(IN_USE, IN_USE_PREPARED);
      assert success;
  }

  /**
   * Marks the transaction as inactive.  This method should only be
   * called when the transaction is in use.
   */
  void setInactive() {
      assert (state.get() & IN_USE) != 0;
      inactive = true;
  }

  /* -- Implement Transaction -- */

  public byte[] getId() {
      return longToBytes(tid);
  }

  public long getCreationTime() {
      return creationTime;
  }

  public long getTimeout() {
      return timeout;
  }

  public void checkTimeout() {
      if (inactive) {
    throw new TransactionNotActiveException(
        "The transaction is not active");
      } else if ((state.get() & PREPARED) != 0) {
    return;
      }
      long runningTime = System.currentTimeMillis() - getCreationTime();
      if (runningTime > getTimeout()) {
    throw new TransactionTimeoutException(
        "Transaction timed out: " + runningTime + " ms");
      }
  }

  public void join(TransactionParticipant participant) {
      this.participant = participant;
  }

  public void abort(Throwable cause) {
      if (cause == null) {
          throw new NullPointerException("Cause cannot be null");
      }
      if (!aborting) {
    aborting = true;
    abortCause = cause;
    participant.abort(this);
      }
  }

  public boolean isAborted() {
      return aborting;
  }

  public Throwable getAbortCause() {
      return abortCause;
  }

  /**
   * Don't bother to support transaction listeners for just the data
   * store, which doesn't use them.
   */
  public void registerListener(TransactionListener listener) {
      throw new UnsupportedOperationException(
    "DataStoreServerImpl doesn't support transaction listeners");
  }

  /* -- Other methods -- */

  public String toString() {
      return "Txn[stid:" + tid + "]";
  }

  public boolean equals(Object object) {
      return (object instanceof Txn) && tid == ((Txn) object).tid;
  }

  public int hashCode() {
      return (int) (tid >>> 32) ^ (int) tid;
  }

  /** Returns a byte array that represents the specified long. */
  private byte[] longToBytes(long l) {
      return new byte[] {
    (byte) (l >>> 56), (byte) (l >>> 48), (byte) (l >>> 40),
    (byte) (l >>> 32), (byte) (l >>> 24), (byte) (l >>> 16),
    (byte) (l >>> 8), (byte) l };
  }
    }

    /**
     * A table for storing transactions that uses a sorted map keyed on
     * transaction IDs.  Since transaction IDs are allocated in order, the
     * transaction IDs will order the transactions by creation time.
     */
    private static class TxnTable<T> {

  /**
   * Maps transaction IDs to transactions and their associated
   * information.
   */
  final NavigableMap<Long, Txn> table =
      new ConcurrentSkipListMap<Long, Txn>();

  /** Creates an instance. */
  TxnTable() { }

  /**
   * Gets the transaction associated with the specified ID, and marks it
   * in use.  Checks if the transaction has timed out if checkTimeout is
   * true, and considers transactions being reaped as not active.
   */
  Txn get(long tid, boolean checkTimeout) {
      Txn txn = table.get(tid);
      if (txn != null) {
    if (checkTimeout) {
        txn.checkTimeout();
    }
    if (!txn.setInUse(true)) {
        throw new IllegalStateException(
      "Multiple simultaneous accesses to transaction: " +
      txn);
    }
    if (!txn.getReaping()) {
        return txn;
    }
      }
      throw new TransactionNotActiveException(
    "Transaction is not active");
  }

  /** Marks the transaction as not in use. */
  void notInUse(Txn txn) {
      boolean ok = txn.setInUse(false);
      /*
       * Only the single transaction that placed the transaction in-use
       * should be setting it not in-use, unless it is marked REAPING,
       * which causes the call to always return true.
       * -tjb@sun.com (02/14/2007)
       */
      assert ok : "Clearing transaction in-use flag failed";
  }
 
  /**
   * Returns all expired transactions that are not in use, marking their
   * states as REAPING.
   */
  Collection<Transaction> getExpired() {
      long now = System.currentTimeMillis();
      Collection<Transaction> result = new ArrayList<Transaction>();
      Long nextId;
      /* Get the first key */
      try {
    nextId = table.firstKey();
      } catch (NoSuchElementException e) {
    nextId = null;
      }
      /* Loop while there is another potentially expired entry */
      while (nextId != null) {
    Txn txn = table.get(nextId);
    if (txn != null &&
        txn.getCreationTime() + txn.getTimeout() < now &&
        txn.setReaping())
    {
        result.add(txn);
    }
    /* Search for the next entry */
    nextId = table.higherKey(nextId);
      }
      return result;
  }
    }

    /**
     * Customize DataStoreImpl to use a different transaction table and to
     * create transactions explicitly.
     */
    private class CustomDataStoreImpl extends DataStoreImpl {

  /** Object to synchronize on when accessing nextTxnId and lastTxnId. */
  private final Object tidLock = new Object();

  /**
   * The next transaction ID to use for allocation Transaction IDs.
   * Valid if not greater than lastTxnId.
   */
  private long nextTxnId = 0;

  /**
   * The last transaction ID that is free for allocating an transaction
   * ID before needing to obtain more IDs from the database.
   */
  private long lastTxnId = -1;

  /**
   * An implementation of TxnInfoTable that uses TxnTable's map keyed on
   * transaction IDs.
   */
  private class CustomTxnInfoTable<T> extends TxnTable<T>
      implements TxnInfoTable<T>
  {
      /** Creates an instance. */
      CustomTxnInfoTable() { }

      /* -- Implement TxnInfoTable -- */

      public T get(Transaction txn) {
    assert txn instanceof Txn;
    Txn t = (Txn) txn;
    /* All transactions will have information of the right type */
    @SuppressWarnings("unchecked")
        T info = (T) t.getTxnInfo();
    if (info != null) {
        return info;
    }
    throw new TransactionNotActiveException(
        "Transaction is not active");
      }

      public T remove(Transaction txn) {
    assert txn instanceof Txn;
    Txn t = (Txn) txn;
    @SuppressWarnings("unchecked")
        T info = (T) t.getTxnInfo();
    long tid = t.getTid();
    Txn t2 = table.remove(tid);
    if (t2 != null) {
        if (!t2.equals(t)) {
      throw new IllegalStateException("Wrong transaction");
        }
        t.setTxnInfo(null);
        return info;
    }
    throw new TransactionNotActiveException(
        "Transaction is not active");
      }

      public void set(Transaction txn, T info) {
    assert txn instanceof Txn;
    Txn t = (Txn) txn;
    t.setTxnInfo(info);
    table.put(t.getTid(), t);
      }
  }

  /** Creates an instance. */
  CustomDataStoreImpl(Properties properties) {
      super(properties, new NullAccessCoordinator());
  }

  /**
   * Create an alternative transaction table, and store it in the
   * txnTable field of the containing server.
   */
  protected <T> TxnInfoTable<T> getTxnInfoTable(Class<T> txnInfoType) {
      CustomTxnInfoTable<T> table = new CustomTxnInfoTable<T>();
      txnTable = table;
      return table;
  }

  /** Creates a new transaction. */
  long createTransaction(long timeout) {
      if (logger.isLoggable(Level.FINER)) {
    logger.log(Level.FINER,
         "createTransaction timeout:{0,number,#}",
         timeout);
      }
      try {
    long tid;
    synchronized (tidLock) {
        if (nextTxnId > lastTxnId) {
      logger.log(
          Level.FINE, "Allocate more transaction IDs");
      nextTxnId = getNextTxnId(
          TXN_ALLOCATION_BLOCK_SIZE, timeout);
      lastTxnId = nextTxnId + TXN_ALLOCATION_BLOCK_SIZE - 1;
        }
        tid = nextTxnId++;
    }
    joinNewTransaction(new Txn(tid, timeout));
    if (logger.isLoggable(Level.FINER)) {
        logger.log(
      Level.FINER,
      "createTransaction timeout:{0,number,#} returns " +
      "stid:{1,number,#}",
      timeout, tid);
    }
    return tid;
      } catch (RuntimeException e) {
    if (logger.isLoggable(Level.FINER)) {
        logger.logThrow(
      Level.FINER, e,
      "createTransaction timeout:{0,number,#} throws",
      timeout);
    }
    throw e;
      }
  }

  /**
   * {@inheritDoc} <p>
   *
   * This implementation does logging and checks that {@code name} is not
   * {@code null}, otherwise delegating to the superclass.
   */
  protected BindingValue getBindingInternal(
      Transaction txn, String name)
  {
      if (logger.isLoggable(FINEST)) {
    logger.log(FINEST, "getBinding txn:{0}, name:{1}", txn, name);
      }
      try {
    checkNull("name", name);
    BindingValue result = super.getBindingInternal(txn, name);
    if (logger.isLoggable(FINEST)) {
        logger.log(FINEST,
             "getBinding txn:{0}, name:{1} returns " +
             "oid:{2,number,#}",
             txn, name, result.getObjectId());
    }
    return result;
      } catch (RuntimeException e) {
    throw handleException(
        txn, FINEST, e,
        "getBinding txn:" + txn + ", name:" + name);
      }
  }

  /**
   * {@inheritDoc} <p>
   *
   * This implementation does logging and checks that {@code name} is not
   * {@code null}, otherwise delegating to the superclass.
   */
  protected BindingValue setBindingInternal(
      Transaction txn, String name, long oid)
  {
      if (logger.isLoggable(FINEST)) {
    logger.log(
        FINEST, "setBinding txn:{0}, name:{1}, oid:{2,number,#}",
        txn, name, oid);
      }
      try {
    checkNull("name", name);
    BindingValue result = super.setBindingInternal(txn, name, oid);
    if (logger.isLoggable(FINEST)) {
        logger.log(FINEST,
             "setBinding txn:{0}, name:{1}," +
             " oid:{2,number,#} returns",
             txn, name, oid);
    }
    return result;
      } catch (RuntimeException e) {
    throw handleException(txn, FINEST, e,
              "setBinding txn:" + txn + ", name:" +
              name + ", oid:" + oid);
      }
  }

  /**
   * {@inheritDoc} <p>
   *
   * This implementation does logging and checks that {@code name} is not
   * {@code null}, otherwise delegating to the superclass.
   */
  protected BindingValue removeBindingInternal(
      Transaction txn, String name)
  {
      if (logger.isLoggable(FINEST)) {
    logger.log(
        FINEST, "removeBinding txn:{0}, name:{1}", txn, name);
      }
      try {
    checkNull("name", name);
    BindingValue result = super.removeBindingInternal(txn, name);
    if (logger.isLoggable(FINEST)) {
        logger.log(FINEST,
             "removeBinding txn:{0}, name:{1} returns {2}",
             txn, name, result);
    }
    return result;
      } catch (RuntimeException e) {
    throw handleException(txn, FINEST, e,
              "removeBinding txn:" + txn +
              ", name:" + name);
      }
  }
    }

    /**
     * An alternative exporter that uses an experimental socket-based facility
     * instead of Java RMI.
     */
    private static class SocketExporter extends Exporter<DataStoreServer> {
  private DataStoreServerRemote remote;
  SocketExporter(Class<DataStoreServer> type) {
      super(type);
  }
  public int export(DataStoreServer server, String name, int port)
      throws IOException
  {
      remote = new DataStoreServerRemote(server, port);
      return remote.getLocalPort();
  }
  public void unexport() {
      if (remote == null) {
    return;
      }
      try {
    remote.shutdown();
    remote = null;
      } catch (IOException e) {
    logger.logThrow(
        Level.FINE, e, "Problem shutting down server");
    return;
      }
  }
    }

    /**
     * Creates an instance of this class configured with the specified
     * properties.  See the {@link DataStoreServerImpl class documentation} for
     * a list of supported properties.
     *
     * @param  properties the properties for configuring this instance
     * @throws  DataStoreException if there is a problem with the database
     * @throws  IllegalArgumentException if the value of the {@code
     *        com.sun.sgs.impl.service.data.store.net.DataStoreServerImpl.port}
     *        property is less than {@code 0} or greater than {@code 65535},
     *        or if thrown by the {@link
     *        DataStoreImpl#DataStoreImpl DataStoreImpl constructor}
     * @throws  IOException if a network problem occurs
     */
    public DataStoreServerImpl(Properties properties) throws IOException {
  logger.log(Level.CONFIG, "Creating DataStoreServerImpl properties:{0}",
       properties);
  PropertiesWrapper wrappedProps = new PropertiesWrapper(properties);
  store = new CustomDataStoreImpl(properties);
  maxTxnTimeout = wrappedProps.getLongProperty(
      MAX_TXN_TIMEOUT_PROPERTY, DEFAULT_MAX_TXN_TIMEOUT,
      1, Long.MAX_VALUE);
  int requestedPort = wrappedProps.getIntProperty(
      PORT_PROPERTY, DEFAULT_PORT, 0, 65535);
  exporter = noRmi ?
      new SocketExporter(DataStoreServer.class) :
      new Exporter<DataStoreServer>(DataStoreServer.class);
  port = exporter.export(this, "DataStoreServer", requestedPort);
  if (requestedPort == 0) {
      logger.log(Level.INFO, "Server is using port {0,number,#}", port);
  }
  long reapDelay = wrappedProps.getLongProperty(
      REAP_DELAY_PROPERTY, DEFAULT_REAP_DELAY);
  executor = Executors.newSingleThreadScheduledExecutor();
  executor.scheduleAtFixedRate(
      new Runnable() {
    public void run() {
        try {
      reapExpiredTransactions();
        } catch (Throwable t) {
      logger.logThrow(
          Level.WARNING, t,
          "Problem reaping expired transactions");
        }
    }
      },
      reapDelay, reapDelay, TimeUnit.MILLISECONDS);
    }

    /* -- Implement DataStoreServer -- */

    /** {@inheritDoc} */
    public long createObject(long tid) {
  Txn txn = getTxn(tid);
  try {
      return store.createObject(txn);
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public void markForUpdate(long tid, long oid) {
  Txn txn = getTxn(tid);
  try {
      store.markForUpdate(txn, oid);
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public byte[] getObject(long tid, long oid, boolean forUpdate) {
  Txn txn = getTxn(tid);
  try {
      return store.getObject(txn, oid, forUpdate);
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public void setObject(long tid, long oid, byte[] data) {
  Txn txn = getTxn(tid);
  try {
      store.setObject(txn, oid, data);
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public void setObjects(long tid, long[] oids, byte[][] dataArray) {
  Txn txn = getTxn(tid);
  try {
      store.setObjects(txn, oids, dataArray);
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public void removeObject(long tid, long oid) {
  Txn txn = getTxn(tid);
  try {
      store.removeObject(txn, oid);
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public BindingValue getBinding(long tid, String name) {
  Txn txn = getTxn(tid);
  try {
      return store.getBindingInternal(txn, name);
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public BindingValue setBinding(long tid, String name, long oid) {
  Txn txn = getTxn(tid);
  try {
      return store.setBindingInternal(txn, name, oid);
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public BindingValue removeBinding(long tid, String name) {
  Txn txn = getTxn(tid);
  try {
      return store.removeBindingInternal(txn, name);
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public String nextBoundName(long tid, String name) {
  Txn txn = getTxn(tid);
  try {
      return store.nextBoundName(txn, name);
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public int getClassId(long tid, byte[] classInfo) {
  Txn txn = getTxn(tid);
  try {
      return store.getClassId(txn, classInfo);
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public byte[] getClassInfo(long tid, int classId)
  throws ClassInfoNotFoundException
    {
  Txn txn = getTxn(tid);
  try {
      return store.getClassInfo(txn, classId);
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public long nextObjectId(long tid, long oid) {
  Txn txn = getTxn(tid);
  try {
      return store.nextObjectId(txn, oid);
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public long createTransaction(long timeout) {
  if (timeout <= 0) {
      throw new IllegalArgumentException(
    "Timeout must be greater than zero: " + timeout);
  }
  return store.createTransaction(Math.min(timeout, maxTxnTimeout));
    }

    /** {@inheritDoc} */
    public boolean prepare(long tid) {
  Txn txn = getTxn(tid);
  try {
      boolean result = store.prepare(txn);
      txn.setPrepared();
      return result;
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public void commit(long tid) {
  Txn txn = getTxn(tid, false);
  try {
      store.commit(txn);
      txn.setInactive();
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public void prepareAndCommit(long tid) {
  Txn txn = getTxn(tid);
  try {
      store.prepareAndCommit(txn);
      txn.setInactive();
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /** {@inheritDoc} */
    public void abort(long tid) {
  Txn txn = getTxn(tid, false);
  try {
      store.abort(txn);
      txn.setInactive();
  } finally {
      txnTable.notInUse(txn);
  }
    }

    /* -- Other public methods -- */

    /**
     * Shuts down this server. Calls to this method will block until the
     * shutdown is complete.
     *
     */
    public synchronized void shutdown() {
        store.shutdown();
  executor.shutdownNow();
  exporter.unexport();
    }

    /**
     * Returns the port being used for the server.
     *
     * @return  the port
     */
    public int getPort() {
  return port;
    }

    /**
     * Returns a string representation of this object.
     *
     * @return  a string representation of this object
     */
    public String toString() {
  return "DataStoreServerImpl[store:" + store + ", port:" + port + "]";
    }

    /* -- Package access and private methods -- */

    /**
     * Find transactions that are expired and not in use, and tell the data
     * store to abort them.  The data store needs this nudging to notice that
     * it can perform the abort.
     */
    void reapExpiredTransactions() {
  /*
   * Note that a transaction is only marked REAPING if it has expired and
   * is not currently in use.  All subsequent operations will notice that
   * it is expired, either because the transaction has been aborted, or
   * because they will perform their own expiration check.  As a result,
   * the only database access for that transaction will be to abort.
   * Whether the reaper will be the one to perform the abort or another
   * request to the server will do it doesn't matter as far as avoiding
   * concurrent access to the transaction because the abort operation
   * atomically removes the transaction from the transaction table.
   * -tjb@sun.com (02/14/2007)
   */
  Collection<Transaction> expired = txnTable.getExpired();
  for (Transaction txn : expired) {
      try {
    store.abort(txn);
      } catch (TransactionNotActiveException e) {
    /*
     * The abort call should fail this way because this is an
     * already expired exception.
     */
      } catch (TransactionTimeoutException e) {
    /* The transaction already timed out */
      }
  }
  int numExpired = expired.size();
  if (numExpired > 0) {
      logger.log(
    Level.FINE, "Reaped {0} expired transactions", numExpired);
  }
    }

    /**
     * Returns the transaction for the specified ID, throwing
     * TransactionNotActiveException if the transaction is not active, and
     * checking whether the transaction has timed out.
     */
    private Txn getTxn(long tid) {
  return getTxn(tid, true);
    }

    /**
     * Returns the transaction for the specified ID, throwing
     * IllegalArgumentException if the ID is negative, throwing
     * TransactionNotActiveException if the transaction is not active, and
     * checking, if requested, whether the transaction has timed out.  Treats
     * transactions that are being reaped as being not active.
     */
    private Txn getTxn(long tid, boolean checkTimeout) {
  if (tid < 0) {
      throw new IllegalArgumentException(
    "The transaction ID must not be negative: " + tid);
  }
  try {
      return txnTable.get(tid, checkTimeout);
  } catch (RuntimeException e) {
      logger.logThrow(Level.FINE, e,
          "Getting transaction stid:{0,number,#} failed",
          tid);
      throw e;
  }
    }
}
TOP

Related Classes of com.sun.sgs.impl.service.data.store.net.DataStoreServerImpl$SocketExporter

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.
etElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');