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;
}
/**
* 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)
{
String value = this.propertyDefaults.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;
}
/**
* {@inheritDoc}
*/
public void afterPropertiesSet() throws Exception
{
// Default the category to the bean name
if (this.category == null)
{
if (this.beanName == null)
{
throw new IllegalStateException("Category not provided");
}
this.category = this.beanName;
}
// Derive the unique ID from the category and instance path
Listfalse
, 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