/**
* Helios, OpenSource Monitoring
* Brought to you by the Helios Development Group
*
* Copyright 2007, Helios Development Group and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
*/
package org.helios.apmrouter.server;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.apache.log4j.Logger;
import org.helios.apmrouter.jmx.JMXHelper;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.naming.SelfNaming;
import org.springframework.jmx.export.notification.NotificationPublisher;
import org.springframework.jmx.export.notification.NotificationPublisherAware;
/**
* <p>Title: ServerComponentBean</p>
* <p>Description: </p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.apmrouter.server.ServerComponentBean</code></p>
*/
@ManagedResource
public abstract class ServerComponentBean extends ServerComponent implements
ApplicationContextLifecycleListener,
ApplicationContextAware,
BeanNameAware,
SmartApplicationListener,
ApplicationEventMulticaster,
SelfNaming,
InitializingBean,
NotificationPublisherAware,
DisposableBean {
/** The application context for this bean */
protected GenericApplicationContext applicationContext = null;
/** The bean name for this bean */
protected String beanName = null;
/** The delegate app multicaster */
protected SimpleApplicationEventMulticaster eventMulticaster = null;
/** The event supporting executor */
protected Executor eventExecutor = null;
/** The ordering priority of this component */
protected int priority = Ordered.LOWEST_PRECEDENCE;
/** The JMX ObjectName for this component */
protected ObjectName objectName = null;
/** The started state of this component */
protected final AtomicBoolean started = new AtomicBoolean(false);
/** Application Event types that this component accepts */
protected final Set<Class<? extends ApplicationEvent>> supportedEventTypes = new HashSet<Class<? extends ApplicationEvent>>();
/** Source types that this component accepts application events from */
protected final Set<Class<?>> supportedEventSourceTypes = new HashSet<Class<?>>();
/** The notification publisher */
protected NotificationPublisher notificationPublisher = null;
/**
* Creates a new ServerComponentBean.
* Inspects the application context methods, and if overriden in this class, adds the type to the supported event types.
*/
protected ServerComponentBean() {
testAppEventSupport("onApplicationContextClose", ContextClosedEvent.class, ContextClosedEvent.class);
testAppEventSupport("onApplicationContextRefresh", ContextRefreshedEvent.class, ContextRefreshedEvent.class);
testAppEventSupport("onApplicationContextStart", ContextStartedEvent.class, ContextStartedEvent.class);
testAppEventSupport("onApplicationContextStop", ContextStoppedEvent.class, ContextStoppedEvent.class);
}
/**
* Returns a declared method for this class
* @param name The name of the method
* @param params The parameter types of the method
* @return the method or null if one was not found
*/
protected Method getDeclaredMethod(String name, Class<?>...params) {
try {
return getClass().getDeclaredMethod(name, params);
} catch (Exception ex) {
return null;
}
}
/**
* Looks up the method defined by <code>name</code> and <params> and if overriden, adds the <code>supported</code> class to the supported event types.
* @param name The name of the method to lookup
* @param supported The event type being tested for
* @param params The parameter types of the method to lookup
*/
protected void testAppEventSupport(String name, Class<? extends ApplicationEvent> supported, Class<?>...params) {
Method method = getDeclaredMethod(name, params);
if(method!=null && method.getDeclaringClass()==getClass()) {
supportedEventTypes.add(supported);
}
}
/**
* {@inheritDoc}
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
*/
@Override
public void onApplicationEvent(ApplicationEvent event) {
debug("Received ApplicationEvent [", event, "]");
}
public ObjectName getObjectName(Object managedBean, String beanKey) throws MalformedObjectNameException {
StringBuilder b = new StringBuilder(getClass().getPackage().getName());
b.delete(b.lastIndexOf("."), b.length()-1);
objectName = JMXHelper.objectName(b.append(":service=").append(getClass().getSimpleName()).append(",name=").append(beanName));
return objectName;
}
public ObjectName getObjectName() {
StringBuilder b = new StringBuilder(getClass().getPackage().getName());
b.delete(b.lastIndexOf("."), b.length());
objectName = JMXHelper.objectName(b.append(":service=").append(getClass().getSimpleName()).append(",name=").append(beanName));
return objectName;
}
/**
* {@inheritDoc}
* @see org.springframework.core.Ordered#getOrder()
*/
@Override
public int getOrder() {
return priority;
}
/**
* {@inheritDoc}
* @see org.springframework.context.event.SmartApplicationListener#supportsEventType(java.lang.Class)
*/
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
if(supportedEventTypes.isEmpty()) return false;
return supportedEventTypes.contains(eventType);
}
/**
* {@inheritDoc}
* @see org.springframework.context.event.SmartApplicationListener#supportsSourceType(java.lang.Class)
*/
@Override
public boolean supportsSourceType(Class<?> sourceType) {
if(supportedEventSourceTypes.isEmpty()) return true;
return supportedEventSourceTypes.contains(sourceType);
}
/**
* {@inheritDoc}
* @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String)
*/
@Override
public void setBeanName(String name) {
beanName = name;
log = Logger.getLogger(getClass().getName() + "." + beanName);
}
/**
* Returns the bean name
* @return the bean name
*/
@ManagedAttribute(description="The bean name of this component")
public String getBeanName() {
return beanName;
}
protected final ApplicationListener lifecycleListener = new ApplicationListener<ApplicationContextEvent>() {
@Override
public void onApplicationEvent(final ApplicationContextEvent event) {
if(applicationContext != event.getApplicationContext()) return;
if(event instanceof ContextStartedEvent) {
eventExecutor.execute(new Runnable(){
public void run() { onApplicationContextStart((ContextStartedEvent)event); }
});
} else if(event instanceof ContextRefreshedEvent) {
eventExecutor.execute(new Runnable(){
public void run() { onApplicationContextRefresh((ContextRefreshedEvent)event); }
});
} else if(event instanceof ContextStoppedEvent) {
eventExecutor.execute(new Runnable(){
public void run() { onApplicationContextStop((ContextStoppedEvent)event); }
});
} else {
eventExecutor.execute(new Runnable(){
public void run() { onApplicationContextClose((ContextClosedEvent)event); }
});
}
}
};
/**
* {@inheritDoc}
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(applicationContext instanceof GenericApplicationContext) {
this.applicationContext = (GenericApplicationContext)applicationContext;
final GenericApplicationContext appCtx = this.applicationContext;
this.applicationContext.addApplicationListener(lifecycleListener);
eventMulticaster = new SimpleApplicationEventMulticaster(applicationContext);
if(eventExecutor!=null) {
eventMulticaster.setTaskExecutor(eventExecutor);
}
} else {
throw new IllegalArgumentException("This bean requires a GenericApplicationContext but was passed a [" + applicationContext.getClass().getName() + "]", new Throwable());
}
}
/**
* Add a listener to be notified of all events.
* @param listener The listener to add
* @see org.springframework.context.event.AbstractApplicationEventMulticaster#addApplicationListener(org.springframework.context.ApplicationListener)
*/
@Override
public void addApplicationListener(@SuppressWarnings("rawtypes") ApplicationListener listener) {
if(eventMulticaster==null) throw new IllegalStateException("This component's eventMulticaster has not been initialized yet", new Throwable());
eventMulticaster.addApplicationListener(listener);
}
/**
* Add a listener bean to be notified of all events.
* @param listenerBeanName The name of the bean to register
* @see org.springframework.context.event.AbstractApplicationEventMulticaster#addApplicationListenerBean(java.lang.String)
*/
@Override
public void addApplicationListenerBean(String listenerBeanName) {
if(eventMulticaster==null) throw new IllegalStateException("This component's eventMulticaster has not been initialized yet", new Throwable());
eventMulticaster.addApplicationListenerBean(listenerBeanName);
}
/**
* Removes an application listener
* @param listener the listener to remove
* @see org.springframework.context.event.AbstractApplicationEventMulticaster#removeApplicationListener(org.springframework.context.ApplicationListener)
*/
@Override
public void removeApplicationListener(@SuppressWarnings("rawtypes") ApplicationListener listener) {
if(eventMulticaster==null) throw new IllegalStateException("This component's eventMulticaster has not been initialized yet", new Throwable());
eventMulticaster.removeApplicationListener(listener);
}
/**
* Multicast the given application event to appropriate listeners.
* @param event The event to multicast
* @see org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent)
*/
@Override
public void multicastEvent(ApplicationEvent event) {
if(eventMulticaster==null) throw new IllegalStateException("This component's eventMulticaster has not been initialized yet", new Throwable());
eventMulticaster.multicastEvent(event);
}
/**
* Removes an application listener bean
* @param listenerBeanName The bean name of the listener to remove
* @see org.springframework.context.event.AbstractApplicationEventMulticaster#removeApplicationListenerBean(java.lang.String)
*/
@Override
public void removeApplicationListenerBean(String listenerBeanName) {
if(eventMulticaster==null) throw new IllegalStateException("This component's eventMulticaster has not been initialized yet", new Throwable());
eventMulticaster.removeApplicationListenerBean(listenerBeanName);
}
/**
* Removes all registered listeners
* @see org.springframework.context.event.AbstractApplicationEventMulticaster#removeAllListeners()
*/
@Override
public void removeAllListeners() {
if(eventMulticaster==null) throw new IllegalStateException("This component's eventMulticaster has not been initialized yet", new Throwable());
eventMulticaster.removeAllListeners();
}
/**
* Sets the spring event executor
* @param eventExecutor the eventExecutor to set
*/
@Autowired(required=false)
@Qualifier("SpringEvent")
public void setEventExecutor(Executor eventExecutor) {
this.eventExecutor = eventExecutor;
if(eventMulticaster!=null) {
eventMulticaster.setTaskExecutor(eventExecutor);
}
}
/**
* Called when bean is started.
*/
@Override
@ManagedOperation
public final void start() throws Exception {
try {
super.start();
if(isStarted()) throw new IllegalStateException("Cannot start component once it is started", new Throwable());
info(banner("Starting [", beanName, "]"));
doStart();
started.set(true);
info(banner("Started [", beanName, "]"));
} catch (Exception e) {
error("Failed to start [", beanName, "]", e);
throw e;
}
}
/**
* Called when bean is stopped
*/
@Override
@ManagedOperation
public final void stop() {
if(!isStarted()) throw new IllegalStateException("Cannot stop component once it is stopped", new Throwable());
try {
info(banner("Stopping [", beanName, "]"));
doStop();
info(banner("Stopped [", beanName, "]"));
} finally {
started.set(false);
}
super.stop();
}
/**
* To be implemented by concrete classes that have a specific start operation
* @throws Exception thrown if startup fails
*/
protected void doStart() throws Exception {}
/**
* To be implemented by concrete classes that have a specific stop operation
*/
protected void doStop(){};
/**
* Indicates if this component is started
* @return true if this component is started, false otherwise
*/
@ManagedAttribute
public boolean isStarted() {
return started.get();
}
/**
* {@inheritDoc}
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
@Override
public void destroy() throws Exception {
stop();
}
/**
* {@inheritDoc}
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
start();
}
/**
* Returns this components JMX ObjectName
* @return the objectName
*/
public ObjectName getComponentObjectName() {
return objectName;
}
/**
* Sets this components JMX ObjectName
* @param objectName the objectName to set
*/
public void setObjectName(ObjectName objectName) {
this.objectName = objectName;
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.server.ApplicationContextLifecycleListener#onApplicationContextStart(org.springframework.context.event.ContextStartedEvent)
*/
@Override
public void onApplicationContextStart(ContextStartedEvent event) {
trace("AppCtx [", event.getApplicationContext().getDisplayName(), "] Started");
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.server.ApplicationContextLifecycleListener#onApplicationContextRefresh(org.springframework.context.event.ContextRefreshedEvent)
*/
@Override
public void onApplicationContextRefresh(ContextRefreshedEvent event) {
trace("AppCtx [", event.getApplicationContext().getDisplayName(), "] Refreshed");
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.server.ApplicationContextLifecycleListener#onApplicationContextStop(org.springframework.context.event.ContextStoppedEvent)
*/
@Override
public void onApplicationContextStop(ContextStoppedEvent event) {
trace("AppCtx [", event.getApplicationContext().getDisplayName(), "] Stopped");
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.server.ApplicationContextLifecycleListener#onApplicationContextClose(org.springframework.context.event.ContextClosedEvent)
*/
@Override
public void onApplicationContextClose(ContextClosedEvent event) {
trace("AppCtx [", event.getApplicationContext().getDisplayName(), "] Closed");
}
/**
* Sets the notification publisher
* @param notificationPublisher the notificationPublisher to set
*/
@Override
public void setNotificationPublisher(NotificationPublisher notificationPublisher) {
this.notificationPublisher = notificationPublisher;
}
}