true when there is a nested call to either of these
     * methods. This is the case when there is an MBean AND one of these method was
     * NOT originally called from that MBean (it is a local code).
     */
    private ThreadLocaltrue when there is a nested call back from
    * a JMX bean.
    */
    private ThreadLocaltrue if the bean should be started on startup of the parent application context
     */
    public void setAutoStart(boolean autoStart)
    {
        this.autoStart = autoStart;
    }
    /**
     * Sets the property defaults provided by the installer or System properties.
     * 
     * @param propertyDefaults
     *            the property defaults
     */
    public void setPropertyDefaults(Properties propertyDefaults)
    {
        this.propertyDefaults = propertyDefaults;
    }
    
    public void setEncryptedPropertyDefaults(Properties propertyDefaults)
    {
        this.encryptedPropertyDefaults = propertyDefaults;
        
    }
    /**
     * Gets the property defaults provided by the installer or System properties.
     * 
     * @return the property defaults
     */
    protected Properties getPropertyDefaults()
    {
        return this.propertyDefaults;
    }
    /**
     * Resolves the default value of a property, if there is one, expanding placholders as necessary.
     * 
     * @param name
     *            the property name
     * @return the resolved default value or null if there isn't one
     */
    protected String resolveDefault(String name)
    {
        Properties props = new Properties();
        
        if(propertyDefaults != null)
        {
            for( Object key : propertyDefaults.keySet())
            {
                props.setProperty((String)key, propertyDefaults.getProperty((String)key));
            }
        }
        
        if(encryptedPropertyDefaults != null)
        {
            for( Object key : encryptedPropertyDefaults.keySet())
            {
                props.setProperty((String)key, encryptedPropertyDefaults.getProperty((String)key));
            }
        }
        
        String value = props.getProperty(name);
        if (value != null)
        {
            value = this.defaultResolver.resolveValue(value);
        }
        return value;
    }
    /**
     * Gets the parent application context.
     * 
     * @return the parent application context
     */
    protected ApplicationContext getParent()
    {
        return this.parent;
    }
    /**
     * Gets the state.
     * 
     * @param start
     *            are we making use of the state? I.e. should we start it if it has not been already?
     * @return the state
     */
    protected PropertyBackedBeanState getState(boolean start)
    {
        if (start)
        {
            start(true, false);
        }
        return this.state;
    }
    
    /**
     *  When set to true, calls to setProperties / setProperty are persisted to the MBean if it exists.
     */
    public void setSaveSetProperty(boolean saveSetProperty)
    {
        this.saveSetProperty = saveSetProperty;
    }
    /**
     * Check properties for invalid values using {@link SubsystemEarlyPropertyChecker}s
     * @param properties
     * @return The complete error message in case of exceptions or empty string otherwise
     */
    public String performEarlyPropertyChecks(Mapfalse, whereas on the removal of a dynamically created instance, this
     *            value would be true.
     */
    protected void destroy(boolean isPermanent)
    {
        if (this.runtimeState != RuntimeState.UNINITIALIZED)
        {
            boolean hadWriteLock = this.lock.isWriteLockedByCurrentThread();
            if (!hadWriteLock)
            {
                this.lock.readLock().unlock();
                this.lock.writeLock().lock();
            }
            try
            {
                if (this.runtimeState != RuntimeState.UNINITIALIZED)
                {
                    logger.debug("destroy() stop state="+runtimeState);
                    stop(false);
                    logger.debug("destroy() deregister "+isPermanent);
                    this.registry.deregister(this, isPermanent);
                    this.state = null;
                    this.runtimeState = RuntimeState.UNINITIALIZED;
                    logger.debug("destroy() done");
                }
            }
            finally
            {
                logger.debug("destroy() state="+runtimeState);
                if (!hadWriteLock)
                {
                    this.lock.readLock().lock();
                    this.lock.writeLock().unlock();
                }
            }
        }
    }
    /**
     * {@inheritDoc}
     */
    public boolean isUpdateable(String name)
    {
        return true;
    }
    /**
     * {@inheritDoc}
     */
    public String getDescription(String name)
    {
        return isUpdateable(name) ? "Editable Property " + name : "Read-only Property " + name;
    }
    /**
     * {@inheritDoc}
     */
    public void onApplicationEvent(ApplicationEvent event)
    {
        if (this.autoStart && event instanceof ContextRefreshedEvent && event.getSource() == this.parent)
        {
            this.lock.writeLock().lock();
            try
            {
                start(false, false);
            }
            catch (Exception e)
            {
                // Let's log and swallow auto-start exceptions so that they are non-fatal. This means that the system
                // can hopefully be brought up to a level where its configuration can be edited and corrected
                logger.error("Error auto-starting subsystem", e);
            }
            finally
            {
                this.lock.writeLock().unlock();
            }
        }
        else if (event instanceof PropertyBackedBeanStartedEvent)
        {
            this.lock.writeLock().lock();
            try
            {
                // If we aren't started, reinitialize so that we pick up state changes from the database
                switch (this.runtimeState)
                {
                case PENDING_BROADCAST_START:
                case STOPPED:
                    destroy(false);
                    // fall through
                case UNINITIALIZED:
                    start(false, false);                
                }
            }
            finally
            {
                this.lock.writeLock().unlock();                
            }
        }
        else if (event instanceof PropertyBackedBeanStoppedEvent)
        {
            this.lock.writeLock().lock();
            try
            {
                // Completely destroy the state so that it will have to be reinitialized should the bean be put back in
                // to use by this node
                destroy(false);
            }
            finally
            {
                this.lock.writeLock().unlock();
            }
        }
    }
    /**
     * {@inheritDoc}
     */
    public String getProperty(String name)
    {
        this.lock.readLock().lock();
        try
        {
            doInit();
            return this.state.getProperty(name);
        }
        finally
        {
            this.lock.readLock().unlock();
        }
    }
    /**
     * {@inheritDoc}
     */
    public SetWhen called from code within the local node the values are saved to the * database in the Enterprise edition and will be visible in a JMX client.
* * @param name * @param value */ public void setProperty(String name, String value) { if (logger.isDebugEnabled()) { logger.debug("setProperty("+name+','+value+")"); } if (!nestedCall.get()) { nestedCall.set(true); this.lock.writeLock().lock(); try { boolean mBeanInfoChange = !getPropertyNames().contains(name); // When setting properties locally AND there is an MBean, the following broadcast // results in a call to the MBean's setAttributes method, which in turn results // in a nested call back. The call back sets the values in this bean and // localSetProperties will be set to true. The MBean persists the changes and the // broadcast method returns. If there is no MBean (community edition) OR when // initiated from the MBean (say setting a value via JConsole), nothing happens // as a result of the broadcast. if (saveSetProperty) { logger.debug("setProperty() broadcastSetProperties"); this.registry.broadcastSetProperty(this, name, value); } if (localSetProperties.get()) { if (mBeanInfoChange) { // Re register the bean so new properties are visible in JConsole which does // not check MBeanInfo for changes otherwise. logger.debug("setProperty() destroy"); // Commented out to avoid "UserTransaction is not visible from class loader" as it drops the context. // So we have to just live with the JConsole as it is. // destroy(false); // Attempt to start locally start(false, true); } } else { logger.debug("setProperty() setPropertyInternal"); setPropertyInternal(name, value); } } finally { localSetProperties.set(false); nestedCall.set(false); this.lock.writeLock().unlock(); } } else { // A nested call indicates there is a MBean and that this method was // NOT originally called from that MBean. localSetProperties.set(true); logger.debug("setProperty() callback setPropertyInternal"); setPropertyInternal(name, value); } } /** * {@inheritDoc} * *
When called from code within the local node the values are saved to the * database in the Enterprise edition and will be visible in a JMX client.
     *
     * @param properties to be saved.
     */
    public void setProperties(Map