Package com.sleepycat.je

Source Code of com.sleepycat.je.EnvironmentMutableConfig

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002, 2011 Oracle and/or its affiliates.  All rights reserved.
*
*/

package com.sleepycat.je;

import java.io.Serializable;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Properties;

import com.sleepycat.je.config.ConfigParam;
import com.sleepycat.je.config.EnvironmentParams;
import com.sleepycat.je.dbi.DbConfigManager;
import com.sleepycat.je.dbi.EnvironmentImpl;

/**
* Specifies the environment attributes that may be changed after the
* environment has been opened.  EnvironmentMutableConfig is a parameter to
* {@link Environment#setMutableConfig} and is returned by {@link
* Environment#getMutableConfig}.
*
* <p>There are two types of mutable environment properties: per-environment
* handle properties, and environment wide properties.</p>
*
* <h4>Per-Environment Handle Properties</h4>
*
* <p>Per-environment handle properties apply only to a single Environment
* instance.  For example, to change the default transaction commit behavior
* for a single environment handle, do this:</p>
*
* <blockquote><pre>
*     // Specify no-sync behavior for a given handle.
*     EnvironmentMutableConfig mutableConfig = myEnvHandle.getMutableConfig();
*     mutableConfig.setTxnNoSync(true);
*     myEnvHandle.setMutableConfig(mutableConfig);
* </pre></blockquote>
*
* <p>The per-environment handle properties are listed below.  These properties
* are accessed using the setter and getter methods listed, as shown in the
* example above.</p>
*
* <ul>
* <li>{@link #setDurability}, {@link #getDurability}</li>
* <li>{@link #setTxnNoSync}, {@link #getTxnNoSync} <em>deprecated</em></li>
* <li>{@link #setTxnWriteNoSync}, {@link #getTxnWriteNoSync} <em>deprecated</em></li>
* </ul>
*
* <h4>Environment-Wide Mutable Properties</h4>
*
* <p>Environment-wide mutable properties are those that can be changed for an
* environment as a whole, irrespective of which environment instance (for the
* same physical environment) is used.  For example, to stop the cleaner daemon
* thread, do this:</p>
*
* <blockquote><pre>
*     // Stop the cleaner daemon thread for the environment.
*     EnvironmentMutableConfig mutableConfig = myEnvHandle.getMutableConfig();
*     mutableConfig.setConfigParam("je.env.runCleaner", "false");
*     myEnvHandle.setMutableConfig(mutableConfig);
* </pre></blockquote>
*
* <p>The environment-wide mutable properties are listed below.  These
* properties are accessed using the {@link #setConfigParam} and {@link
* #getConfigParam} methods, as shown in the example above, using the property
* names listed below.  In some cases setter and getter methods are also
* available.</p>
*
* <ul>
* <li>je.maxMemory ({@link #setCacheSize}, {@link #getCacheSize})</li>
* <li>je.maxMemoryPercent ({@link #setCachePercent},
* {@link #getCachePercent})</li>
* <li>je.env.runINCompressor</li>
* <li>je.env.runEvictor</li>
* <li>je.env.runCheckpointer</li>
* <li>je.env.runCleaner</li>
* </ul>
*
* <h4>Getting the Current Environment Properties</h4>
*
* To get the current "live" properties of an environment after constructing it
* or changing its properties, you must call {@link Environment#getConfig} or
* {@link Environment#getMutableConfig}.  The original EnvironmentConfig or
* EnvironmentMutableConfig object used to set the properties is not kept up to
* date as properties are changed, and does not reflect property validation or
* properties that are computed. @see EnvironmentConfig
*/
public class EnvironmentMutableConfig implements Cloneable, Serializable {
    private static final long serialVersionUID = 1L;

    /*
     * Change copyHandlePropsTo and Environment.copyToHandleConfig when adding
     * fields here.
     */
    private boolean txnNoSync = false;
    private boolean txnWriteNoSync = false;

    /**
     * @hidden
     * Cache size is a category of property that is calculated within the
     * environment.  It is only supplied when returning the cache size to the
     * application and never used internally; internal code directly checks
     * with the MemoryBudget class;
     */
    protected long cacheSize;

    /**
     * @hidden
     * Note that in the implementation we choose not to extend Properties in
     * order to keep the configuration type safe.
     */
    protected Properties props;

    /**
     * For unit testing, to prevent loading of je.properties.
     */
    private transient boolean loadPropertyFile = true;

    /**
     * Internal boolean that says whether or not to validate params.  Setting
     * it to false means that parameter value validatation won't be performed
     * during setVal() calls.  Only should be set to false by unit tests using
     * DbInternal.
     */
    transient boolean validateParams = true;

    private transient ExceptionListener exceptionListener = null;
    private CacheMode cacheMode;

    /**
     * @hidden
     * Avoid doc build errors.
     */
    private CacheModeStrategy cacheModeStrategy;

    /**
     * An instance created using the default constructor is initialized with
     * the system's default settings.
     */
    public EnvironmentMutableConfig() {
        props = new Properties();
    }

    /**
     * Used by EnvironmentConfig to construct from properties.
     */
    EnvironmentMutableConfig(Properties properties)
        throws IllegalArgumentException {

        DbConfigManager.validateProperties(properties,
                                           false,  // isRepConfigInstance
                                           getClass().getName());
        /* For safety, copy the passed in properties. */
        props = new Properties();
        props.putAll(properties);
    }

    /**
     * Configures the database environment for asynchronous transactions.
     *
     * @param noSync If true, do not write or synchronously flush the log on
     * transaction commit. This means that transactions exhibit the ACI
     * (Atomicity, Consistency, and Isolation) properties, but not D
     * (Durability); that is, database integrity is maintained, but if the JVM
     * or operating system fails, it is possible some number of the most
     * recently committed transactions may be undone during recovery. The
     * number of transactions at risk is governed by how many updates fit into
     * a log buffer, how often the operating system flushes dirty buffers to
     * disk, and how often the database environment is checkpointed.
     *
     * <p>This attribute is false by default for this class and for the
     * database environment.</p>
     *
     * @deprecated replaced by {@link #setDurability}
     */
    public EnvironmentMutableConfig setTxnNoSync(boolean noSync) {
        setTxnNoSyncVoid(noSync);
        return this;
    }
   
    /**
     * @hidden
     * The void return setter for use by Bean editors.
     */
    public void setTxnNoSyncVoid(boolean noSync) {
        TransactionConfig.checkMixedMode
            (false, noSync, txnWriteNoSync, getDurability());
        txnNoSync = noSync;
    }

    /**
     * Returns true if the database environment is configured for asynchronous
     * transactions.
     *
     * @return true if the database environment is configured for asynchronous
     * transactions.
     *
     * @deprecated replaced by {@link #getDurability}
     */
    public boolean getTxnNoSync() {
        return txnNoSync;
    }

    /**
     * Configures the database environment for transactions which write but do
     * not flush the log.
     *
     * @param writeNoSync If true, write but do not synchronously flush the log
     * on transaction commit. This means that transactions exhibit the ACI
     * (Atomicity, Consistency, and Isolation) properties, but not D
     * (Durability); that is, database integrity is maintained, but if the
     * operating system fails, it is possible some number of the most recently
     * committed transactions may be undone during recovery. The number of
     * transactions at risk is governed by how often the operating system
     * flushes dirty buffers to disk, and how often the database environment is
     * checkpointed.
     *
     * <p>The motivation for this attribute is to provide a transaction that
     * has more durability than asynchronous (nosync) transactions, but has
     * higher performance than synchronous transactions.</p>
     *
     * <p>This attribute is false by default for this class and for the
     * database environment.</p>
     *
     * @deprecated replaced by {@link #setDurability}
     */
    public EnvironmentMutableConfig setTxnWriteNoSync(boolean writeNoSync) {
        setTxnWriteNoSyncVoid(writeNoSync);
        return this;
    }
   
    /**
     * @hidden
     * The void return setter for use by Bean editors.
     */
    public void setTxnWriteNoSyncVoid(boolean writeNoSync) {
        TransactionConfig.checkMixedMode
            (false, txnNoSync, writeNoSync, getDurability());
        txnWriteNoSync = writeNoSync;
    }

    /**
     * Returns true if the database environment is configured for transactions
     * which write but do not flush the log.
     *
     * @return true if the database environment is configured for transactions
     * which write but do not flush the log.
     *
     * @deprecated replaced by {@link #getDurability}
     */
    public boolean getTxnWriteNoSync() {
        return txnWriteNoSync;
    }

    /**
     * Configures the durability associated with transactions.
     * <p>
     * Equivalent to setting the je.txn.durability property in the
     * je.properties file.
     * </p>
     * @see Durability
     *
     * @param durability the new durability definition
     *
     * @return this
     */
    public EnvironmentMutableConfig setDurability(Durability durability) {
        setDurabilityVoid(durability);
        return this;
    }
   
    /**
     * @hidden
     * The void return setter for use by Bean editors.
     */
    public void setDurabilityVoid(Durability durability) {
        TransactionConfig.checkMixedMode
            (false, txnNoSync, txnWriteNoSync, durability);

        if (durability == null) {
            props.remove(EnvironmentParams.JE_DURABILITY);
        } else {
            DbConfigManager.setVal(props, EnvironmentParams.JE_DURABILITY,
                                   durability.toString(),
                                   validateParams);
        }
    }

    /**
     * Returns the durability associated with the configuration.
     *
     * @return the durability setting currently associated with this config.
     */
    public Durability getDurability() {
        String value = DbConfigManager.getVal(props,
                                              EnvironmentParams.JE_DURABILITY);
        return Durability.parse(value);
    }

    /**
     * Configures the memory available to the database system, in bytes.
     *
     * <p>Equivalent to setting the je.maxMemory property in the je.properties
     * file. The system will evict database objects when it comes within a
     * prescribed margin of the limit.</p>
     *
     * <p>By default, JE sets the cache size to:</p>
     *
     * <pre><blockquote>
     *         je.maxMemoryPercent *  JVM maximum memory
     * </pre></blockquote>
     *
     * <p>where JVM maximum memory is specified by the JVM -Xmx flag. However,
     * calling setCacheSize() with a non-zero value overrides the percentage
     * based calculation and sets the cache size explicitly.</p>
     *
     * <p>Note that the log buffer cache may be cleared if the cache size is
     * changed after the environment has been opened.</p>
     *
     * <p>If setSharedCache(true) is called, setCacheSize and setCachePercent
     * specify the total size of the shared cache, and changing these
     * parameters will change the size of the shared cache.</p>
     * <p>
     * To take full advantage of JE cache memory, it is strongly recommended
     * that
     * <a href="http://download.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html#compressedOop">compressed oops</a>
     * (<code>-XX:+UseCompressedOops</code>) is specified when a 64-bit JVM is
     * used and the maximum heap size is less than 32 GB.  As described in the
     * referenced documentation, compressed oops is sometimes the default JVM
     * mode even when it is not explicitly specified in the Java command.
     * However, if compressed oops is desired then it <em>must</em> be
     * explicitly specified in the Java command when running DbCacheSize or a
     * JE application.  If it is not explicitly specified then JE will not
     * aware of it, even if it is the JVM default setting, and will not take it
     * into account when calculating cache memory sizes.
     *
     * @param totalBytes The memory available to the database system, in bytes.
     *
     * @throws IllegalArgumentException if an invalid parameter is specified.
     *
     * @return this
     */
    public EnvironmentMutableConfig setCacheSize(long totalBytes)
        throws IllegalArgumentException {

        setCacheSizeVoid(totalBytes);
        return this;
    }
   
    /**
     * @hidden
     * The void return setter for use by Bean editors.
     */
    public void setCacheSizeVoid(long totalBytes)
        throws IllegalArgumentException {

        DbConfigManager.setVal(props, EnvironmentParams.MAX_MEMORY,
                               Long.toString(totalBytes), validateParams);
    }

    /**
     * Returns the memory available to the database system, in bytes. A valid
     * value is only available if this EnvironmentConfig object has been
     * returned from Environment.getConfig();
     *
     * @return The memory available to the database system, in bytes.
     */
    public long getCacheSize() {

        /*
         * CacheSize is filled in from the EnvironmentImpl by way of
         * fillInEnvironmentGeneratedProps.
         */
        return cacheSize;
    }

    /**
     * <p>By default, JE sets its cache size proportionally to the JVM
     * memory. This formula is used:</p>
     *
     * <blockquote><pre>
     *         je.maxMemoryPercent *  JVM maximum memory
     * </pre></blockquote>
     *
     * <p>where JVM maximum memory is specified by the JVM -Xmx flag.
     * setCachePercent() specifies the percentage used and is equivalent to
     * setting the je.maxMemoryPercent property in the je.properties file.</p>
     *
     * <p>Calling setCacheSize() with a non-zero value overrides the percentage
     * based calculation and sets the cache size explicitly.</p>
     *
     * <p>Note that the log buffer cache may be cleared if the cache size is
     * changed after the environment has been opened.</p>
     *
     * <p>If setSharedCache(true) is called, setCacheSize and setCachePercent
     * specify the total size of the shared cache, and changing these
     * parameters will change the size of the shared cache.</p>
     * <p>
     * To take full advantage of JE cache memory, it is strongly recommended
     * that
     * <a href="http://download.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html#compressedOop">compressed oops</a>
     * (<code>-XX:+UseCompressedOops</code>) is specified when a 64-bit JVM is
     * used and the maximum heap size is less than 32 GB.  As described in the
     * referenced documentation, compressed oops is sometimes the default JVM
     * mode even when it is not explicitly specified in the Java command.
     * However, if compressed oops is desired then it <em>must</em> be
     * explicitly specified in the Java command when running DbCacheSize or a
     * JE application.  If it is not explicitly specified then JE will not
     * aware of it, even if it is the JVM default setting, and will not take it
     * into account when calculating cache memory sizes.
     *
     * @param percent The percent of JVM memory to allocate to the JE cache.
     *
     * @throws IllegalArgumentException if an invalid parameter is specified.
     *
     * @return this
     */
    public EnvironmentMutableConfig setCachePercent(int percent)
        throws IllegalArgumentException {

        setCachePercentVoid(percent);
        return this;
    }
   
    /**
     * @hidden
     * The void return setter for use by Bean editors.
     */
    public void setCachePercentVoid(int percent)
        throws IllegalArgumentException {

        DbConfigManager.setIntVal(props, EnvironmentParams.MAX_MEMORY_PERCENT,
                                  percent, validateParams);
    }

    /**
     * Returns the percentage value used in the JE cache size calculation.
     *
     * @return the percentage value used in the JE cache size calculation.
     */
    public int getCachePercent() {

        return DbConfigManager.getIntVal(props,
                                         EnvironmentParams.MAX_MEMORY_PERCENT);
    }

    /**
     * Sets the exception listener for an Environment.  The listener is called
     * when a daemon thread throws an exception, in order to provide a
     * notification mechanism for these otherwise asynchronous exceptions.
     * Daemon thread exceptions are also printed through stderr.
     * <p>
     * Not all daemon exceptions are fatal, and the application bears
     * responsibility for choosing how to respond to the notification. Since
     * exceptions may repeat, the application should also choose how to handle
     * a spate of exceptions. For example, the application may choose to act
     * upon each notification, or it may choose to batch up its responses
     * by implementing the listener so it stores exceptions, and only acts
     * when a certain number have been received.
     * @param exceptionListener the callback to be executed when an exception
     * occurs.
     *
     * @return this
     */
    public EnvironmentMutableConfig
        setExceptionListener(ExceptionListener exceptionListener) {

        setExceptionListenerVoid(exceptionListener);
        return this;
    }
   
    /**
     * @hidden
     * The void return setter for use by Bean editors.
     */
    public void setExceptionListenerVoid(ExceptionListener exceptionListener) {
        this.exceptionListener = exceptionListener;
    }

    /**
     * Returns the exception listener, if set.
     */
    public ExceptionListener getExceptionListener() {
        return exceptionListener;
    }

    /**
     * Sets the default {@code CacheMode} used for operations performed in this
     * environment.  The default cache mode may be overridden on a per-database
     * basis using {@link DatabaseConfig#setCacheMode}, and on a per-record or
     * per-operation basis using {@link Cursor#setCacheMode}.
     *
     * @param cacheMode is the default {@code CacheMode} used for operations
     * performed in this environment.  If {@code null} is specified, {@link
     * CacheMode#DEFAULT} will be used.
     *
     * @see CacheMode for further details.
     *
     * @since 4.0.97
     */
    public EnvironmentMutableConfig setCacheMode(final CacheMode cacheMode) {
        setCacheModeVoid(cacheMode);
        return this;
    }
   
    /**
     * @hidden
     * The void return setter for use by Bean editors.
     */
    public void setCacheModeVoid(final CacheMode cacheMode) {
        this.cacheMode = cacheMode;
    }

    /**
     * Returns the default {@code CacheMode} used for operations performed in
     * this environment, or null if {@link CacheMode#DEFAULT} is used.
     *
     * @return the default {@code CacheMode} used for operations performed on
     * this database, or null if {@link CacheMode#DEFAULT} is used.
     *
     * @see #setCacheMode
     *
     * @since 4.0.97
     */
    public CacheMode getCacheMode() {
        return cacheMode;
    }

    /**
     * @hidden
     * For internal use only.
     *
     * Sets the default {@code CacheModeStrategy} used for operations performed
     * in this environment.
     *
     * @param strategy is the {@code CacheModeStrategy} used for operations
     * performed in this environment.  If {@code null} is specified, then no
     * environment default will be available.
     *
     * @see CacheModeStrategy for further details.
     *
     * @since 4.0.97
     */
    public EnvironmentMutableConfig
        setCacheModeStrategy(final CacheModeStrategy strategy) {
        setCacheModeStrategyVoid(strategy);
        return this;
    }
   
    /**
     * @hidden
     * The void return setter for use by Bean editors.
     */
    public void setCacheModeStrategyVoid(final CacheModeStrategy strategy) {
        cacheModeStrategy = strategy;
    }

    /**
     * @hidden
     * For internal use only.
     *
     * Returns the default {@code CacheModeStrategy} used for operations
     * performed in this environment, or null if the no environment default is
     * available.
     *
     * @return the default {@code CacheModeStrategy} used for operations
     * performed in this environment, or null if the no environment default is
     * available.
     *
     * @see #setCacheModeStrategy
     *
     * @since 4.0.97
     */
    public CacheModeStrategy getCacheModeStrategy() {
        return cacheModeStrategy;
    }

    /**
     * Set this configuration parameter. First validate the value specified for
     * the configuration parameter; if it is valid, the value is set in the
     * configuration.
     *
     * @param paramName the configuration parameter name, one of the String
     * constants in this class
     *
     * @param value The configuration value
     *
     * @return this
     *
     * @throws IllegalArgumentException if the paramName or value is invalid.
     */
    public EnvironmentMutableConfig setConfigParam(String paramName,
                                                   String value)
        throws IllegalArgumentException {

        DbConfigManager.setConfigParam(props,
                                       paramName,
                                       value,
                                       true, /* require mutability. */
                                       validateParams,
                                       false /* forReplication */,
                                       true  /* verifyForReplication */);
        return this;
    }

    /**
     * Returns the value for this configuration parameter.
     *
     * @param paramName a valid configuration parameter, one of the String
     * constants in this class.
     * @return the configuration value.
     * @throws IllegalArgumentException if the paramName is invalid.
     */
    public String getConfigParam(String paramName)
        throws IllegalArgumentException {

       return DbConfigManager.getConfigParam(props, paramName);
    }

    /**
     * @hidden
     * For internal use only.
     */
    public boolean isConfigParamSet(String paramName) {
        return props.containsKey(paramName);
    }

    /*
     * Helpers
     */
    void setValidateParams(boolean validateParams) {
        this.validateParams = validateParams;
    }

    /**
     * @hidden
     * Used by unit tests.
     */
    boolean getValidateParams() {
        return validateParams;
    }

    /**
     * Checks that the immutable values in the environment config used to open
     * an environment match those in the config object saved by the underlying
     * shared EnvironmentImpl.
     * @param handleConfigProps are the config property values that were
     * specified by configuration object from the Environment.
     */
    void checkImmutablePropsForEquality(Properties handleConfigProps)
        throws IllegalArgumentException {

        Iterator<String> iter =
            EnvironmentParams.SUPPORTED_PARAMS.keySet().iterator();
        while (iter.hasNext()) {
            String paramName = iter.next();
            ConfigParam param =
                EnvironmentParams.SUPPORTED_PARAMS.get(paramName);
            assert param != null;
            if (!param.isMutable() && !param.isForReplication()) {
                String paramVal = props.getProperty(paramName);
                String useParamVal = handleConfigProps.getProperty(paramName);
                if ((paramVal != null) ?
                    (!paramVal.equals(useParamVal)) :
                    (useParamVal != null)) {
                    throw new IllegalArgumentException
                        (paramName + " is set to " +
                         useParamVal +
                         " in the config parameter" +
                         " which is incompatible" +
                         " with the value of " +
                         paramVal + " in the" +
                         " underlying environment");
                }
            }
        }
    }

    /**
     * @hidden
     * For internal use only.
     * Overrides Object.clone() to clone all properties, used by this class and
     * EnvironmentConfig.
     */
    @Override
    protected EnvironmentMutableConfig clone() {

        try {
            EnvironmentMutableConfig copy =
                (EnvironmentMutableConfig) super.clone();
            copy.props = (Properties) props.clone();
            return copy;
        } catch (CloneNotSupportedException willNeverOccur) {
            return null;
        }
    }

    /**
     * Used by Environment to create a copy of the application supplied
     * configuration. Done this way to provide non-public cloning.
     */
    EnvironmentMutableConfig cloneMutableConfig() {
        EnvironmentMutableConfig copy = (EnvironmentMutableConfig) clone();
        /* Remove all immutable properties. */
        copy.clearImmutableProps();
        return copy;
    }

    /**
     * Copies the per-handle properties of this object to the given config
     * object.
     */
    void copyHandlePropsTo(EnvironmentMutableConfig other) {
        other.txnNoSync = txnNoSync;
        other.txnWriteNoSync = txnWriteNoSync;
        other.setDurability(getDurability());
    }

    /**
     * Copies all mutable props to the given config object.
     * Unchecked suppress here because Properties don't play well with
     * generics in Java 1.5
     */
    @SuppressWarnings("unchecked")
    void copyMutablePropsTo(EnvironmentMutableConfig toConfig) {

        Properties toProps = toConfig.props;
        Enumeration propNames = props.propertyNames();
        while (propNames.hasMoreElements()) {
            String paramName = (String) propNames.nextElement();
            ConfigParam param =
                EnvironmentParams.SUPPORTED_PARAMS.get(paramName);
            assert param != null;
            if (param.isMutable()) {
                String newVal = props.getProperty(paramName);
                toProps.setProperty(paramName, newVal);
            }
        }
        toConfig.exceptionListener = this.exceptionListener;
        toConfig.cacheMode = this.cacheMode;
        toConfig.cacheModeStrategy = this.cacheModeStrategy;
    }

    /**
     * Fills in the properties calculated by the environment to the given
     * config object.
     */
    void fillInEnvironmentGeneratedProps(EnvironmentImpl envImpl) {
        cacheSize = envImpl.getMemoryBudget().getMaxMemory();
    }

   /**
    * Removes all immutable props.
    * Unchecked suppress here because Properties don't play well with
    * generics in Java 1.5
    */
    @SuppressWarnings("unchecked")
    private void clearImmutableProps() {
        Enumeration propNames = props.propertyNames();
        while (propNames.hasMoreElements()) {
            String paramName = (String) propNames.nextElement();
            ConfigParam param =
                EnvironmentParams.SUPPORTED_PARAMS.get(paramName);
            assert param != null;
            if (!param.isMutable()) {
                props.remove(paramName);
            }
        }
    }

    Properties getProps() {
        return props;
    }

    /**
     * For unit testing, to prevent loading of je.properties.
     */
    void setLoadPropertyFile(boolean loadPropertyFile) {
        this.loadPropertyFile = loadPropertyFile;
    }

    /**
     * For unit testing, to prevent loading of je.properties.
     */
    boolean getLoadPropertyFile() {
        return loadPropertyFile;
    }

    /**
     * Testing support
     * @hidden
     */
    public int getNumExplicitlySetParams() {
        return props.size();
    }

    /**
     * Display configuration values.
     */
    @Override
    public String toString() {
        return ("cacheSize=" + cacheSize + "\n" +
                "txnNoSync=" + txnNoSync + "\n" +
                "txnWriteNoSync=" + txnWriteNoSync + "\n" +
                props.toString() + "\n");
    }
}
TOP

Related Classes of com.sleepycat.je.EnvironmentMutableConfig

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.