Package com.sleepycat.je

Source Code of com.sleepycat.je.Transaction

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002-2010 Oracle.  All rights reserved.
*
*/

package com.sleepycat.je;

import java.util.concurrent.TimeUnit;

import com.sleepycat.je.Durability.ReplicaAckPolicy;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.txn.Locker;
import com.sleepycat.je.txn.Txn;
import com.sleepycat.je.utilint.PropUtil;

/**
* The Transaction object is the handle for a transaction.  Methods off the
* transaction handle are used to configure, abort and commit the transaction.
* Transaction handles are provided to other Berkeley DB methods in order to
* transactionally protect those operations.
*
* <p>A single Transaction may be used to protect operations for any number of
* Databases in a given environment.  However, a single Transaction may not be
* used for operations in more than one distinct environment.</p>
*
* <p>Transaction handles are free-threaded; transactions handles may be used
* concurrently by multiple threads. Once the {@link Transaction#abort
* Transaction.abort} or {@link Transaction#commit Transaction.commit} method
* is called, the handle may not be accessed again, regardless of the success
* or failure of the method, with one exception:  the {@code abort} method may
* be called any number of times to simplify error handling.</p>
*
* <p>To obtain a transaction with default attributes:</p>
*
* <blockquote><pre>
*     Transaction txn = myEnvironment.beginTransaction(null, null);
* </pre></blockquote>
*
* <p>To customize the attributes of a transaction:</p>
*
* <blockquote><pre>
*     TransactionConfig config = new TransactionConfig();
*     config.setReadUncommitted(true);
*     Transaction txn = myEnvironment.beginTransaction(null, config);
* </pre></blockquote>
*/
public class Transaction {

    private Txn txn;
    private final Environment env;
    private final long id;
    private String name;

    /*
     * It's set upon a successful updating replicated commit and identifies the
     * VLSN associated with the commit entry.
     */
    private CommitToken commitToken = null;

    /**
     * Creates a transaction.
     */
    protected Transaction(Environment env, Txn txn) {
        this.env = env;
        this.txn = txn;

        /*
         * Copy the id to this wrapper object so the id will be available
         * after the transaction is closed and the txn field is nulled.
         */
        this.id = txn.getId();
    }

    /**
     * Cause an abnormal termination of the transaction.
     *
     * <p>The log is played backward, and any necessary undo operations are
     * done. Before Transaction.abort returns, any locks held by the
     * transaction will have been released.</p>
     *
     * <p>In the case of nested transactions, aborting a parent transaction
     * causes all children (unresolved or not) of the parent transaction to be
     * aborted.</p>
     *
     * <p>All cursors opened within the transaction must be closed before the
     * transaction is aborted.</p>
     *
     * <p>After this method has been called, regardless of its return, the
     * {@link Transaction} handle may not be accessed again, with one
     * exception:  the {@code abort} method itself may be called any number of
     * times to simplify error handling.</p>
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if the environment has been closed, or
     * cursors associated with the transaction are still open.
     */
    public void abort()
        throws DatabaseException {

        try {
            checkEnv();

            /*
             * If the transaction is already closed, do nothing.  Do not call
             * checkOpen in order to support any number of calls to abort().
             */
            if (txn == null) {
                return;
            }

            env.removeReferringHandle(this);
            txn.abort();

            /* Remove reference to internal txn, so we can reclaim memory. */
            txn = null;
        } catch (Error E) {
            DbInternal.getEnvironmentImpl(env).invalidate(E);
            throw E;
        }
    }

    /**
     * Return the transaction's unique ID.
     *
     * @return The transaction's unique ID.
     */
    public long getId() {
        return id;
    }

    /**
     * This method is intended for use with a replicated environment.
     * <p>
     * It returns the commitToken associated with a successful replicated
     * commit. A null value is returned if the txn was not associated with a
     * replicated environment, or the txn did not result in any changes to the
     * environment. This method should only be called after the transaction
     * has finished.
     * <p>
     * This method is typically used in conjunction with the <code>
     * CommitPointConsistencyPolicy</code>.
     *
     * @return the token used to identify the replicated commit. Return null if
     * the transaction has aborted, or has committed without making any
     * updates.
     *
     * @throws IllegalStateException if the method is called before the
     * transaction has committed or aborted.
     *
     * @see com.sleepycat.je.rep.CommitPointConsistencyPolicy
     */
    public CommitToken getCommitToken()
        throws IllegalStateException {

        if (txn == null) {

            /*
             * The commit token is only legitimate after the transaction is
             * closed. A null txn field means the transaction is closed.
             */
            return commitToken;
        }
       
        throw new IllegalStateException
           ("This transaction is still in progress and a commit token " +
            "is not available");
    }

    /**
     * End the transaction.  If the environment is configured for synchronous
     * commit, the transaction will be committed synchronously to stable
     * storage before the call returns.  This means the transaction will
     * exhibit all of the ACID (atomicity, consistency, isolation, and
     * durability) properties.
     *
     * <p>If the environment is not configured for synchronous commit, the
     * commit will not necessarily have been committed to stable storage before
     * the call returns.  This means the transaction will exhibit the ACI
     * (atomicity, consistency, and isolation) properties, but not D
     * (durability); that is, database integrity will be maintained, but it is
     * possible this transaction may be undone during recovery.</p>
     *
     * <p>All cursors opened within the transaction must be closed before the
     * transaction is committed.</p>
     *
     * <p>If the method encounters an error, the transaction <!-- and all child
     * transactions of the transaction --> will have been aborted when the call
     * returns.</p>
     *
     * <p>After this method has been called, regardless of its return, the
     * {@link Transaction} handle may not be accessed again, with one
     * exception:  the {@code abort} method may be called any number of times
     * to simplify error handling.</p>
     *
     * @throws com.sleepycat.je.rep.InsufficientReplicasException if the master
     * in a replicated environment could not contact a quorum of replicas as
     * determined by the {@link ReplicaAckPolicy}. The application must abort
     * the transaction and can choose to retry it.
     *
     * @throws com.sleepycat.je.rep.InsufficientAcksException if the master in
     * a replicated environment did not receive enough replica acknowledgments,
     * although the commit succeeded locally.
     *
     * @throws com.sleepycat.je.rep.ReplicaWriteException if a write operation
     * was performed with this transaction, but this node is now a Replica.
     *
     * @throws OperationFailureException if this exception occurred earlier and
     * caused the transaction to be invalidated.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if the transaction or environment has been
     * closed, or cursors associated with the transaction are still open.
     */
    public void commit()
        throws DatabaseException {

        try {
            checkEnv();
            checkOpen();
            env.removeReferringHandle(this);
            txn.commit();
            commitToken = txn.getCommitToken();
            /* Remove reference to internal txn, so we can reclaim memory. */
            txn = null;
        } catch (Error E) {
            DbInternal.getEnvironmentImpl(env).invalidate(E);
            throw E;
        }
    }

    /**
     * End the transaction using the specified durability requirements. This
     * requirement overrides any default durability requirements associated
     * with the environment. If the durability requirements cannot be satisfied,
     * an exception is thrown to describe the problem. Please see
     * {@link Durability} for specific exceptions that could result when the
     * durability requirements cannot be satisfied.
     *
     * <p>All cursors opened within the transaction must be closed before the
     * transaction is committed.</p>
     *
     * <p>If the method encounters an error, the transaction <!-- and all child
     * transactions of the transaction --> will have been aborted when the call
     * returns.</p>
     *
     * <p>After this method has been called, regardless of its return, the
     * {@link Transaction} handle may not be accessed again, with one
     * exception:  the {@code abort} method may be called any number of times
     * to simplify error handling.</p>
     *
     * @param durability the durability requirements for this transaction
     *
     * @throws com.sleepycat.je.rep.InsufficientReplicasException if the master
     * in a replicated environment could not contact enough replicas to
     * initiate the commit.
     *
     * @throws com.sleepycat.je.rep.InsufficientAcksException if the master in
     * a replicated environment did not receive enough replica acknowledgments,
     * althought the commit succeeded locally.
     *
     * @throws com.sleepycat.je.rep.ReplicaWriteException if a write operation
     * was performed with this transaction, but this node is now a Replica.
     *
     * @throws OperationFailureException if this exception occurred earlier and
     * caused the transaction to be invalidated.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if the transaction or environment has been
     * closed, or cursors associated with the transaction are still open.
     *
     * @throws IllegalArgumentException if an invalid parameter is specified.
     */
    public void commit(Durability durability)
        throws DatabaseException {

        doCommit(durability, false /* explicitSync */);
    }

    /**
     * End the transaction, writing to stable storage and committing
     * synchronously.  This means the transaction will exhibit all of the ACID
     * (atomicity, consistency, isolation, and durability) properties.
     *
     * <p>This behavior is the default for database environments unless
     * otherwise configured using the {@link
     * com.sleepycat.je.EnvironmentConfig#setTxnNoSync
     * EnvironmentConfig.setTxnNoSync} method.  This behavior may also be set
     * for a single transaction using the {@link
     * com.sleepycat.je.Environment#beginTransaction
     * Environment.beginTransaction} method.  Any value specified to this
     * method overrides both of those settings.</p>
     *
     * <p>All cursors opened within the transaction must be closed before the
     * transaction is committed.</p>
     *
     * <p>If the method encounters an error, the transaction <!-- and all child
     * transactions of the transaction --> will have been aborted when the call
     * returns.</p>
     *
     * <p>After this method has been called, regardless of its return, the
     * {@link Transaction} handle may not be accessed again, with one
     * exception:  the {@code abort} method may be called any number of times
     * to simplify error handling.</p>
     *
     * @throws com.sleepycat.je.rep.InsufficientReplicasException if the master
     * in a replicated environment could not contact enough replicas to
     * initiate the commit.
     *
     * @throws com.sleepycat.je.rep.InsufficientAcksException if the master in
     * a replicated environment did not receive enough replica acknowledgments,
     * althought the commit succeeded locally.
     *
     * @throws com.sleepycat.je.rep.ReplicaWriteException if a write operation
     * was performed with this transaction, but this node is now a Replica.
     *
     * @throws OperationFailureException if this exception occurred earlier and
     * caused the transaction to be invalidated.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if the transaction or environment has been
     * closed, or cursors associated with the transaction are still open.
     */
    public void commitSync()
        throws DatabaseException {

        doCommit(Durability.COMMIT_SYNC, true /* explicitSync */);
    }

    /**
     * End the transaction, not writing to stable storage and not committing
     * synchronously. This means the transaction will exhibit the ACI
     * (atomicity, consistency, and isolation) properties, but not D
     * (durability); that is, database integrity will be maintained, but it is
     * possible this transaction may be undone during recovery.
     *
     * <p>This behavior may be set for a database environment using the {@link
     * com.sleepycat.je.EnvironmentConfig#setTxnNoSync
     * EnvironmentConfig.setTxnNoSync} method or for a single transaction using
     * the {@link com.sleepycat.je.Environment#beginTransaction
     * Environment.beginTransaction} method.  Any value specified to this
     * method overrides both of those settings.</p>
     *
     * <p>All cursors opened within the transaction must be closed before the
     * transaction is committed.</p>
     *
     * <p>If the method encounters an error, the transaction <!-- and all child
     * transactions of the transaction --> will have been aborted when the call
     * returns.</p>
     *
     * <p>After this method has been called, regardless of its return, the
     * {@link Transaction} handle may not be accessed again, with one
     * exception:  the {@code abort} method may be called any number of times
     * to simplify error handling.</p>
     *
     * @throws com.sleepycat.je.rep.InsufficientReplicasException if the master
     * in a replicated environment could not contact enough replicas to
     * initiate the commit.
     *
     * @throws com.sleepycat.je.rep.InsufficientAcksException if the master in
     * a replicated environment did not receive enough replica acknowledgments,
     * althought the commit succeeded locally.
     *
     * @throws com.sleepycat.je.rep.ReplicaWriteException if a write operation
     * was performed with this transaction, but this node is now a Replica.
     *
     * @throws OperationFailureException if this exception occurred earlier and
     * caused the transaction to be invalidated.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if the transaction or environment has been
     * closed, or cursors associated with the transaction are still open.
     */
    public void commitNoSync()
        throws DatabaseException {

        doCommit(Durability.COMMIT_NO_SYNC, true /* explicitSync */);
    }

    /**
     * End the transaction, writing to stable storage but not committing
     * synchronously.  This means the transaction will exhibit the ACI
     * (atomicity, consistency, and isolation) properties, but not D
     * (durability); that is, database integrity will be maintained, but it is
     * possible this transaction may be undone during recovery.
     *
     * <p>This behavior is the default for database environments unless
     * otherwise configured using the {@link
     * com.sleepycat.je.EnvironmentConfig#setTxnNoSync
     * EnvironmentConfig.setTxnNoSync} method.  This behavior may also be set
     * for a single transaction using the {@link
     * com.sleepycat.je.Environment#beginTransaction
     * Environment.beginTransaction} method.  Any value specified to this
     * method overrides both of those settings.</p>
     *
     * <p>All cursors opened within the transaction must be closed before the
     * transaction is committed.</p>
     *
     * <p>If the method encounters an error, the transaction <!-- and all child
     * transactions of the transaction --> will have been aborted when the call
     * returns.</p>
     *
     * <p>After this method has been called, regardless of its return, the
     * {@link Transaction} handle may not be accessed again, with one
     * exception:  the {@code abort} method may be called any number of times
     * to simplify error handling.</p>
     *
     * @throws com.sleepycat.je.rep.InsufficientReplicasException if the master
     * in a replicated environment could not contact enough replicas to
     * initiate the commit.
     *
     * @throws com.sleepycat.je.rep.InsufficientAcksException if the master in
     * a replicated environment did not receive enough replica acknowledgments,
     * althought the commit succeeded locally.
     *
     * @throws com.sleepycat.je.rep.ReplicaWriteException if a write operation
     * was performed with this transaction, but this node is now a Replica.
     *
     * @throws OperationFailureException if this exception occurred earlier and
     * caused the transaction to be invalidated.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if the transaction or environment has been
     * closed, or cursors associated with the transaction are still open.
     */
    public void commitWriteNoSync()
        throws DatabaseException {

        doCommit(Durability.COMMIT_WRITE_NO_SYNC, true /* explicitSync */);
    }

    /**
     * For internal use.
     * @hidden
     */
    public boolean getPrepared() {
        return txn.getPrepared();
    }

    /**
     * Perform error checking and invoke the commit on Txn.
     *
     * @param durability the durability to use for the commit
     * @param explicitSync true if the method was invoked from one of the
     * sync-specific APIs, false if durability was used explicitly. This
     * parameter exists solely to support mixed mode api usage checks.
     *
     * @throws IllegalArgumentException via commit(Durability)
     */
    private void doCommit(Durability durability, boolean explicitSync) {
        try {
            checkEnv();
            checkOpen();
            env.removeReferringHandle(this);
            if (explicitSync) {
                /* A sync-specific api was invoked. */
                if (txn.getExplicitDurabilityConfigured()) {
                    throw new IllegalArgumentException
                        ("Mixed use of deprecated durability API for the " +
                         "transaction commit with the new durability API for" +
                         " TransactionConfig or MutableEnvironmentConfig");
                }
            } else if (txn.getExplicitSyncConfigured()) {
                /* Durability was explicitly configured for commit */
                throw new IllegalArgumentException
                    ("Mixed use of new durability API for the " +
                      "transaction commit with deprecated durability API for" +
                      " TransactionConfig or MutableEnvironmentConfig");
            }
            txn.commit(durability);
            commitToken = txn.getCommitToken();
            /* Remove reference to internal txn, so we can reclaim memory. */
            txn = null;
        } catch (Error E) {
            DbInternal.getEnvironmentImpl(env).invalidate(E);
            throw E;
        }
    }

    /**
     * Returns the timeout value for the transaction lifetime.
     *
     * <p>If {@link #setTxnTimeout(long,TimeUnit)} has not been called to
     * configure the timeout, the environment configuration value ({@link
     * EnvironmentConfig#TXN_TIMEOUT} )is returned.</p>
     *
     * @param unit the {@code TimeUnit} of the returned value. May not be null.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if the transaction or environment has been
     * closed.
     *
     * @throws IllegalArgumentException if the unit is null.
     *
     * @since 4.0
     */
    public long getTxnTimeout(TimeUnit unit)
        throws EnvironmentFailureException,
               IllegalStateException,
               IllegalArgumentException {

        checkEnv();
        checkOpen();
        return PropUtil.millisToDuration((int) txn.getTxnTimeout(), unit);
    }

    /**
     * Configures the timeout value for the transaction lifetime.
     *
     * <p>If the transaction runs longer than this time, the transaction may
     * throw {@link com.sleepycat.je.TransactionTimeoutException
     * TransactionTimeoutException}.</p>
     *
     * <p>Transaction timeouts are checked whenever a thread of control blocks
     * on a lock or when deadlock detection is performed.  For this reason, the
     * accuracy of the timeout depends on the length of the lock timeout and
     * how often deadlock detection is performed.</p>
     *
     * @param timeOut The timeout value for the transaction lifetime. A value
     * of 0 disables timeouts for the transaction, meaning that no limit on the
     * duration of the transaction is enforced.
     *
     * @param unit the {@code TimeUnit} of the timeOut value. May be null only
     * if timeOut is zero.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if the transaction or environment has been
     * closed.
     *
     * @throws IllegalArgumentException if timeOut or unit is invalid.
     *
     * @since 4.0
     */
    public void setTxnTimeout(long timeOut, TimeUnit unit)
        throws IllegalArgumentException, DatabaseException {

        checkEnv();
        checkOpen();
        txn.setTxnTimeout(PropUtil.durationToMillis(timeOut, unit));
    }

    /**
     * Configures the timeout value for the transaction lifetime, with the
     * timeout value specified in microseconds.  This method is equivalent to:
     *
     * <pre>setTxnTimeout(long, TimeUnit.MICROSECONDS);</pre>
     *
     * @deprecated as of 4.0, replaced by {@link #setTxnTimeout(long,
     * TimeUnit)}.
     */
    public void setTxnTimeout(long timeOut)
        throws IllegalArgumentException, DatabaseException {

        setTxnTimeout(timeOut, TimeUnit.MICROSECONDS);
    }

    /**
     * Returns the lock request timeout value for the transaction.
     *
     * <p>If {@link #setLockTimeout(long,TimeUnit)} has not been called to
     * configure the timeout, the environment configuration value ({@link
     * EnvironmentConfig#LOCK_TIMEOUT}) is returned.</p>
     *
     * @param unit the {@code TimeUnit} of the returned value. May not be null.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if the transaction or environment has been
     * closed.
     *
     * @throws IllegalArgumentException if the unit is null.
     *
     * @since 4.0
     */
    public long getLockTimeout(TimeUnit unit)
        throws EnvironmentFailureException,
               IllegalStateException,
               IllegalArgumentException {

        checkEnv();
        checkOpen();
        return PropUtil.millisToDuration((int) txn.getLockTimeout(), unit);
    }

    /**
     * Configures the lock request timeout value for the transaction.
     *
     * <p>If a lock request cannot be granted in this time, the transaction may
     * throw {@link com.sleepycat.je.LockTimeoutException
     * LockTimeoutException}.</p>
     *
     * <p>This method may be called at any time during the life of the
     * application.</p>
     *
     * @param timeOut The lock request timeout value for this transaction.  A
     * value of zero turns off transaction timeouts, meaning that no lock wait
     * time limit is enforced and a deadlocked operation will block
     * indefinitely.  We strongly recommend that a large timeout value, rather
     * than a zero value, is used when deadlocks are not expected.  That way, a
     * timeout exception will be thrown when an unexpected deadlock occurs.
     *
     * @param unit the {@code TimeUnit} of the timeOut value. May be null only
     * if timeOut is zero.
     *
     * @throws EnvironmentFailureException if an unexpected, internal or
     * environment-wide failure occurs.
     *
     * @throws IllegalStateException if the transaction or environment has been
     * closed.
     *
     * @throws IllegalArgumentException if timeOut or unit is invalid.
     *
     * @since 4.0
     */
    public void setLockTimeout(long timeOut, TimeUnit unit)
        throws IllegalArgumentException, DatabaseException {

        checkEnv();
        checkOpen();
        txn.setLockTimeout(PropUtil.durationToMillis(timeOut, unit));
    }

    /**
     * Configures the lock request timeout value for the transaction, with the
     * timeout value specified in microseconds.  This method is equivalent to:
     *
     * <pre>setLockTimeout(long, TimeUnit.MICROSECONDS);</pre>
     *
     * @deprecated as of 4.0, replaced by {@link #setLockTimeout(long,
     * TimeUnit)}.
     */
    public void setLockTimeout(long timeOut)
        throws IllegalArgumentException, DatabaseException {

        setLockTimeout(timeOut, TimeUnit.MICROSECONDS);
    }

    /**
     * Set the user visible name for the transaction.
     *
     * @param name The user visible name for the transaction.
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Get the user visible name for the transaction.
     *
     * @return The user visible name for the transaction.
     */
    public String getName() {
        return name;
    }

    /**
     * For internal use.
     * @hidden
     */
    @Override
    public int hashCode() {
        return (int) id;
    }

    /**
     * For internal use.
     * @hidden
     */
    @Override
    public boolean equals(Object o) {
        if (o == null) {
            return false;
        }

        if (!(o instanceof Transaction)) {
            return false;
        }

        if (((Transaction) o).id == id) {
            return true;
        }

        return false;
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("<Transaction id=\"");
        sb.append(id).append("\"");
        if (name != null) {
            sb.append(" name=\"");
            sb.append(name).append("\"");
            }
        sb.append(">");
        return sb.toString();
    }

    /**
     * This method should only be called by the LockerFactory.getReadableLocker
     * and getWritableLocker methods.  The locker returned does not enforce the
     * readCommitted isolation setting.
     *
     * @throws IllegalArgumentException via all API methods with a txn param
     */
    Locker getLocker()
        throws DatabaseException {

        if (txn == null) {
            throw new IllegalArgumentException
                ("Transaction " + id +
                 " has been closed and is no longer usable.");
        }
        return txn;
    }

    /*
     * Helpers
     */

    Txn getTxn() {
        return txn;
    }

    /**
     * @throws EnvironmentFailureException if the underlying environment is
     * invalid, via all methods.
     *
     * @throws IllegalStateException via all methods.
     */
    private void checkEnv() {
        EnvironmentImpl envImpl =  env.getEnvironmentImpl();
        if (envImpl == null) {
            throw new IllegalStateException
                ("The environment has been closed. " +
                 "This transaction is no longer usable.");
        }
        envImpl.checkIfInvalid();
    }

    /**
     * @throws IllegalStateException via all methods except abort.
     */
    private void checkOpen() {
        if (txn == null) {
            throw new IllegalStateException("Transaction Id " + id +
                                            " has been closed.");
        }
    }

    /**
     * Returns whether this {@code Transaction} is open, valid and can be used.
     * If this method returns false, {@link #abort} should be called as soon as
     * possible.
     *
     * <p>When an {@link OperationFailureException}, or one of its
     * subclasses, is caught, the {@code isValid} method should be called to
     * determine whether the {@code Transaction} can continue to be used, or
     * should be aborted.</p>
     */
    public boolean isValid() {
        return txn != null &&
               txn.isValid();
    }
}
TOP

Related Classes of com.sleepycat.je.Transaction

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.