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

Source Code of com.sun.sgs.impl.service.data.store.net.DataStoreClient$TxnInfo

/*
* 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.TransactionAbortedException;
import com.sun.sgs.app.TransactionNotActiveException;
import com.sun.sgs.app.TransactionTimeoutException;
import com.sun.sgs.impl.kernel.StandardProperties;
import com.sun.sgs.impl.service.data.store.AbstractDataStore;
import com.sun.sgs.impl.service.data.store.BindingValue;
import com.sun.sgs.impl.sharedutil.LoggerWrapper;
import com.sun.sgs.impl.sharedutil.PropertiesWrapper;
import com.sun.sgs.kernel.AccessCoordinator;
import com.sun.sgs.kernel.NodeType;
import com.sun.sgs.service.Transaction;
import com.sun.sgs.service.TransactionParticipant;
import com.sun.sgs.service.store.ClassInfoNotFoundException;
import com.sun.sgs.service.store.DataStore;
import java.io.IOException;
import java.net.InetAddress;
import java.rmi.NotBoundException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Provides an implementation of {@code DataStore} by communicating over the
* network to an implementation of {@link DataStoreServer}, and optionally runs
* the server. <p>
*
* The {@link #DataStoreClient(Properties, AccessCoordinator) 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 that uses the data store will be permitted to run
*  before it is a candidate for being aborted.  This value must be greater
*  than {@code 0}. <p>
*
* <dd style="padding-top: .5em">Whether to run the server by creating an
*  instance of {@link DataStoreServerImpl}, using the properties provided
*  to this instance's constructor. <p>
*
* <dt>  <i>Property:</i> <code><b>
*  com.sun.sgs.impl.service.data.store.net.server.host
</b></code><br>
<i>Default</i> the value of the {@code com.sun.sgs.server.host}
*  property, if present, or {@code localhost} if this node is starting the
*      server.
*
* <dd style="padding-top: .5em">The name of the host running the {@code
*  DataStoreServer}. <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 the {@code
*  DataStoreServer}.  This value must be no less than {@code 0} and no
*  greater than {@code 65535}.  The value {@code 0} can only be specified
*      if the {@code com.sun.sgs.node.type} property is not {@code appNode},
*      and means that an anonymous port will be chosen for running the
*      server. <p>
*
* </dl> <p>
*
* This class uses the {@link Logger} named {@code
* com.sun.sgs.impl.service.data.store.net.client} to log information
* at the following levels: <p>
*
* <ul>
* <li> {@link Level#SEVERE SEVERE} - Problems starting the server
* <li> {@link Level#INFO INFO} - Starting the server
* <li> {@link Level#CONFIG CONFIG} - Constructor properties
* <li> {@link Level#FINE FINE} - Allocating object IDs
* <li> {@link Level#FINEST FINEST} - Object operations
* </ul>
*/
public final class DataStoreClient extends AbstractDataStore {

    /** 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 + ".client"));

    /** The logger for transaction abort exceptions. */
    static final LoggerWrapper abortLogger =
  new LoggerWrapper(Logger.getLogger(PACKAGE + ".client.abort"));

    /** The property that specifies the name of the server host. */
    private static final String SERVER_HOST_PROPERTY =
  PACKAGE + ".server.host";

    /** The property that specifies the server port. */
    private static final String SERVER_PORT_PROPERTY =
  PACKAGE + ".server.port";

    /** The default for the server port. */
    private static final int DEFAULT_SERVER_PORT = 44530;

    /**
     * The number of times to retry attempting to obtain the server after a
     * failure to obtain it initially.
     */
    private static final int GET_SERVER_MAX_RETRIES = 3;

    /**
     * The number of milliseconds to wait between attempts to obtain the
     * server.
     */
    private static final long GET_SERVER_WAIT = 10000;

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

    /** 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. */
    private static final long DEFAULT_MAX_TXN_TIMEOUT = 600000;

    /** The server host name. */
    private final String serverHost;

    /** The server port. */
    private final int serverPort;

    /** The remote server. */
    private final DataStoreServer server;

    /** The local server or null. */
    private DataStoreServerImpl localServer = null;

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

    /** Provides information about the transaction for the current thread. */
    private final ThreadLocal<TxnInfo> threadTxnInfo =
  new ThreadLocal<TxnInfo>();

    /** Object to synchronize on when accessing txnCount and shuttingDown. */
    private final Object txnCountLock = new Object();

    /** The number of currently active transactions. */
    private int txnCount = 0;

    /** Whether the client is in the process of shutting down. */
    private boolean shuttingDown = false;

    /** Stores transaction information. */
    private static class TxnInfo {

  /** The transaction. */
  final Transaction txn;

  /** The associated server transaction ID. */
  final long tid;

  /** Whether preparation of the transaction has started. */
  boolean prepared;

  /** Whether the server side has already aborted. */
  boolean serverAborted;

  /** Creates an instance. */
  TxnInfo(Transaction txn, long tid) {
      this.txn = txn;
      this.tid = tid;
  }
    }

    /**
     * Creates an instance of this class configured with the specified
     * properties and access coordinator.  See the {@link DataStoreClient class
     * documentation} for a list of supported properties.
     *
     * @param  properties the properties for configuring this instance
     * @param  accessCoordinator the access coordinator
     * @throws  IllegalArgumentException if the {@code
     *    com.sun.sgs.impl.service.data.store.net.server.host} property
     *    is not set, or if the value of the {@code
     *    com.sun.sgs.impl.service.data.store.net.server.port} property
     *    is not a valid integer not less than {@code 0} and not greater
     *    than {@code 65535}
     * @throws  IOException if a network problem occurs
     * @throws  NotBoundException if the server is not found in the Java RMI
     *    registry
     */
    public DataStoreClient(Properties properties,
         AccessCoordinator accessCoordinator)
  throws IOException, NotBoundException
    {
  super(accessCoordinator, logger, abortLogger);
  logger.log(Level.CONFIG, "Creating DataStoreClient properties:{0}",
       properties);
  PropertiesWrapper wrappedProps = new PropertiesWrapper(properties);
        NodeType nodeType =
                wrappedProps.getEnumProperty(StandardProperties.NODE_TYPE,
                                             NodeType.class,
                                             NodeType.singleNode);
        boolean serverStart = nodeType != NodeType.appNode;
        if (serverStart) {
            // we default to localHost;  this is useful for starting
            // single node systems
            String localHost = InetAddress.getLocalHost().getHostName();
            serverHost = wrappedProps.getProperty(
                SERVER_HOST_PROPERTY,
                wrappedProps.getProperty(
                    StandardProperties.SERVER_HOST, localHost));
        } else {
            // a server host most be specified
            serverHost = wrappedProps.getProperty(
                SERVER_HOST_PROPERTY,
                wrappedProps.getProperty(
                    StandardProperties.SERVER_HOST));
            if (serverHost == null) {
                throw new IllegalArgumentException(
                                           "A server host must be specified");
            }
        }
  int specifiedServerPort = wrappedProps.getIntProperty(
      SERVER_PORT_PROPERTY, DEFAULT_SERVER_PORT, serverStart ? 0 : 1,
      65535);
  maxTxnTimeout = wrappedProps.getLongProperty(
      MAX_TXN_TIMEOUT_PROPERTY, DEFAULT_MAX_TXN_TIMEOUT, 1,
      Long.MAX_VALUE);
  if (serverStart) {
      try {
    localServer = new DataStoreServerImpl(properties);
    serverPort = localServer.getPort();
    logger.log(Level.INFO, "Started server: {0}", localServer);
      } catch (IOException t) {
    logger.logThrow(Level.SEVERE, t, "Problem starting server");
    throw t;
      } catch (RuntimeException t) {
    logger.logThrow(Level.SEVERE, t, "Problem starting server");
    throw t;
      }
  } else {
      serverPort = specifiedServerPort;
  }
  server = getServer();
    }

    /* -- Implement AbstractDataStore's DataStore methods -- */

    /** {@inheritDoc} */
    protected long createObjectInternal(Transaction txn) {
  try {
      TxnInfo txnInfo = checkTxn(txn);
      return server.createObject(txnInfo.tid);
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /** {@inheritDoc} */
    protected void markForUpdateInternal(Transaction txn, long oid) {
  try {
      TxnInfo txnInfo = checkTxn(txn);
      server.markForUpdate(txnInfo.tid, oid);
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /** {@inheritDoc} */
    protected byte[] getObjectInternal(
  Transaction txn, long oid, boolean forUpdate)
    {
  try {
      TxnInfo txnInfo = checkTxn(txn);
      return server.getObject(txnInfo.tid, oid, forUpdate);
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /** {@inheritDoc} */
    protected void setObjectInternal(Transaction txn, long oid, byte[] data) {
  try {
      TxnInfo txnInfo = checkTxn(txn);
      server.setObject(txnInfo.tid, oid, data);
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /** {@inheritDoc} */
    protected void setObjectsInternal(
  Transaction txn, long[] oids, byte[][] dataArray)
    {
  try {
      TxnInfo txnInfo = checkTxn(txn);
      server.setObjects(txnInfo.tid, oids, dataArray);
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /** {@inheritDoc} */
    protected void removeObjectInternal(Transaction txn, long oid) {
  try {
      TxnInfo txnInfo = checkTxn(txn);
      server.removeObject(txnInfo.tid, oid);
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /** {@inheritDoc} */
    protected BindingValue getBindingInternal(Transaction txn, String name) {
  try {
      TxnInfo txnInfo = checkTxn(txn);
      return server.getBinding(txnInfo.tid, name);
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /** {@inheritDoc} */
    protected BindingValue setBindingInternal(
  Transaction txn, String name, long oid)
    {
  try {
      TxnInfo txnInfo = checkTxn(txn);
      return server.setBinding(txnInfo.tid, name, oid);
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /** {@inheritDoc} */
    protected BindingValue removeBindingInternal(
  Transaction txn, String name)
    {
  try {
      TxnInfo txnInfo = checkTxn(txn);
      return server.removeBinding(txnInfo.tid, name);
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /** {@inheritDoc} */
    protected String nextBoundNameInternal(Transaction txn, String name) {
  try {
      TxnInfo txnInfo = checkTxn(txn);
      return server.nextBoundName(txnInfo.tid, name);
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /** {@inheritDoc} */
    protected void shutdownInternal() {
  synchronized (txnCountLock) {
      shuttingDown = true;
      while (txnCount > 0) {
    try {
        logger.log(Level.FINEST,
             "shutdown waiting for {0} transactions",
             txnCount);
        txnCountLock.wait();
    } catch (InterruptedException e) {
        // loop until shutdown is complete
        logger.log(Level.FINEST, "Interrupt ignored during" +
             "shutdown");
    }
      }
      if (txnCount < 0) {
    return; // return silently
      }
     
      txnCount = -1;
      if (localServer != null) {
    localServer.shutdown();
    localServer = null;
      }
  }
    }

    /** {@inheritDoc} */
    protected int getClassIdInternal(Transaction txn, byte[] classInfo) {
  try {
      TxnInfo txnInfo = checkTxn(txn);
      return server.getClassId(txnInfo.tid, classInfo);
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /** {@inheritDoc} */
    protected byte[] getClassInfoInternal(Transaction txn, int classId)
  throws ClassInfoNotFoundException
    {
  try {
      TxnInfo txnInfo = checkTxn(txn);
      return server.getClassInfo(txnInfo.tid, classId);
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /** {@inheritDoc} */
    protected long nextObjectIdInternal(Transaction txn, long oid) {
  try {
      TxnInfo txnInfo = checkTxn(txn);
      return server.nextObjectId(txnInfo.tid, oid);
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /* -- Implement AbstractDataStore's TransactionParticipant methods -- */

    /** {@inheritDoc} */
    protected boolean prepareInternal(Transaction txn) {
  try {
      TxnInfo txnInfo = checkTxnNoJoin(txn, true);
      checkTimeout(txn);
      if (txnInfo.prepared) {
    throw new IllegalStateException(
        "Transaction has already been prepared");
      }
      boolean result = server.prepare(txnInfo.tid);
      txnInfo.prepared = true;
      if (result) {
    threadTxnInfo.set(null);
    decrementTxnCount();
      }
      return result;
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /** {@inheritDoc} */
    protected void commitInternal(Transaction txn) {
  try {
      TxnInfo txnInfo = checkTxnNoJoin(txn, true);
      if (!txnInfo.prepared) {
    throw new IllegalStateException(
        "Transaction has not been prepared");
      }
      server.commit(txnInfo.tid);
      threadTxnInfo.set(null);
      decrementTxnCount();
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /** {@inheritDoc} */
    protected void prepareAndCommitInternal(Transaction txn) {
  try {
      TxnInfo txnInfo = checkTxnNoJoin(txn, true);
      checkTimeout(txn);
      if (txnInfo.prepared) {
    throw new IllegalStateException(
        "Transaction has already been prepared");
      }
      server.prepareAndCommit(txnInfo.tid);
      threadTxnInfo.set(null);
      decrementTxnCount();
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }

    /** {@inheritDoc} */
    protected void abortInternal(Transaction txn) {
  try {
      TxnInfo txnInfo = checkTxnNoJoin(txn, false);
      if (!txnInfo.serverAborted) {
    try {
        server.abort(txnInfo.tid);
    } catch (TransactionNotActiveException e) {
        logger.logThrow(Level.FINEST, e,
            "abort txn:{0} - Transaction already " +
            "aborted by server",
            txn);
    }
      }
      threadTxnInfo.set(null);
      decrementTxnCount();
  } catch (IOException e) {
      throw new NetworkException("", e);
  }
    }
   
    /* -- Other AbstractDataStore methods -- */

    /**
     * {@inheritDoc} <p>
     *
     * In addition to the operations performed by the superclass, this
     * implementation includes the operation in the exception message for
     * {@link NetworkException}s, converts {@link
     * TransactionNotActiveException} to {@link TransactionTimeoutException} if
     * the transaction timeout has passed, and notes in the information for the
     * transaction if the transaction has been aborted on the server side.
     */
    @Override
    protected RuntimeException handleException(Transaction txn,
                 Level level,
                 RuntimeException e,
                 String operation)
    {
  if (e instanceof NetworkException) {
      /* Include the operation in the message */
      Throwable cause = e.getCause();
      e = new NetworkException(
    operation + " failed due to a communication problem: " +
    cause.getMessage(), cause);
  } else if (e instanceof TransactionNotActiveException && txn != null) {
      /*
       * If the transaction is not active on the server, then it may have
       * timed out.
       */
      long duration = System.currentTimeMillis() - txn.getCreationTime();
      if (duration > txn.getTimeout()) {
    e = new TransactionTimeoutException(
        operation + " failed: Transaction timed out after " +
        duration + " ms",
        e);
      }
  }
  /*
   * If we're throwing an exception saying that the transaction was
   * aborted, then note that the server must already know about the
   * abort.
   */
  if (e instanceof TransactionAbortedException) {
      TxnInfo txnInfo = threadTxnInfo.get();
      if (txnInfo != null) {
    txnInfo.serverAborted = true;
      }
  }
  return super.handleException(txn, level, e, operation);
    }

    /* -- Other public methods -- */

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

    /* -- Private methods -- */

    /** Obtains the server. */
    private DataStoreServer getServer() throws IOException, NotBoundException {
  boolean done = false;
  for (int i = 0; !done; i++) {
      if (i == GET_SERVER_MAX_RETRIES) {
    done = true;
      }
      try {
    if (!noRmi) {
        Registry registry = LocateRegistry.getRegistry(
      serverHost, serverPort);
        return (DataStoreServer) registry.lookup(
      "DataStoreServer");
    } else {
        return new DataStoreClientRemote(serverHost, serverPort);
    }
      } catch (IOException e) {
    if (done) {
        throw e;
    }
      } catch (NotBoundException e) {
    if (done) {
        throw e;
    }
      }
  }
  throw new AssertionError();
    }

    /**
     * Checks that the correct transaction is in progress, and join if none is
     * in progress.
     */
    private TxnInfo checkTxn(Transaction txn) throws IOException {
  if (txn == null) {
      throw new NullPointerException("Transaction must not be null");
  }
  TxnInfo txnInfo = threadTxnInfo.get();
  if (txnInfo == null) {
      txnInfo = joinTransaction(txn);
  } else if (!txnInfo.txn.equals(txn)) {
      throw new IllegalStateException(
    "Wrong transaction: Found " + txnInfo.txn +
    ", expected " + txn);
  } else if (txnInfo.prepared) {
      throw new IllegalStateException("Transaction has been prepared");
  }
  checkTimeout(txn);
  return txnInfo;
    }

    /**
     * Joins the specified transaction, checking first to see if the data store
     * is currently shutting down, and returning the new TxnInfo.
     */
    private TxnInfo joinTransaction(Transaction txn) throws IOException {
  synchronized (txnCountLock) {
      if (txnCount < 0) {
    throw new IllegalStateException("Service is shut down");
      } else if (shuttingDown) {
    throw new IllegalStateException("Service is shutting down");
      }
      txnCount++;
  }
  boolean joined = false;
  long tid = -1;
  try {
      tid = server.createTransaction(txn.getTimeout());
      txn.join(this);
      joined = true;
      if (logger.isLoggable(Level.FINER)) {
    logger.log(Level.FINER,
         "Created server transaction stid:{0,number,#} " +
         "for transaction {1}",
         tid, txn);
      }
  } finally {
      if (!joined) {
    decrementTxnCount();
    if (tid != -1) {
        try {
      server.abort(tid);
        } catch (RuntimeException e) {
      if (logger.isLoggable(Level.FINEST)) {
          logger.logThrow(
        Level.FINEST, e,
        "Problem aborting server transaction " +
        "stid:{0,number,#} for transaction {1}",
        tid, txn);
      }
        }
    }
      }
  }
  TxnInfo txnInfo = new TxnInfo(txn, tid);
  threadTxnInfo.set(txnInfo);
  return txnInfo;
    }

    /**
     * Checks that the correct transaction is in progress, throwing an
     * exception if the transaction has not been joined.  If notAborting is
     * true, then checks if the store is shutting down.
     */
    private TxnInfo checkTxnNoJoin(Transaction txn, boolean notAborting) {
  if (txn == null) {
      throw new NullPointerException("Transaction must not be null");
  }
  TxnInfo txnInfo = threadTxnInfo.get();
  if (txnInfo == null) {
      throw new IllegalStateException("Transaction is not active");
  } else if (notAborting && getTxnCount() < 0) {
      throw new IllegalStateException("DataStore is shutting down");
  } else if (!txnInfo.txn.equals(txn)) {
      throw new IllegalStateException("Wrong transaction");
  }
  return txnInfo;
    }

    /** Returns the current transaction count. */
    private int getTxnCount() {
  synchronized (txnCountLock) {
      return txnCount;
  }
    }

    /** Decrements the current transaction count. */
    private void decrementTxnCount() {
  synchronized (txnCountLock) {
      txnCount--;
      if (txnCount <= 0) {
    txnCountLock.notifyAll();
      }
  }
    }

    /**
     * Checks that the transaction has not timed out, including if it has run
     * for longer than the maximum timeout.
     */
    private void checkTimeout(Transaction txn) {
  long max = Math.min(txn.getTimeout(), maxTxnTimeout);
  long runningTime = System.currentTimeMillis() - txn.getCreationTime();
  if (runningTime > max) {
      throw new TransactionTimeoutException(
    "Transaction timed out: " + runningTime + " ms");
  }
    }
}
TOP

Related Classes of com.sun.sgs.impl.service.data.store.net.DataStoreClient$TxnInfo

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.