View Javadoc

1   package cz.cuni.amis.pogamut.base.agent.jmx;
2   
3   import java.util.LinkedHashSet;
4   import java.util.Set;
5   import java.util.logging.Level;
6   
7   import javax.management.InstanceAlreadyExistsException;
8   import javax.management.MBeanRegistrationException;
9   import javax.management.MBeanServer;
10  import javax.management.MalformedObjectNameException;
11  import javax.management.NotCompliantMBeanException;
12  import javax.management.ObjectName;
13  
14  import com.google.inject.Inject;
15  
16  import cz.cuni.amis.pogamut.base.agent.IAgent;
17  import cz.cuni.amis.pogamut.base.agent.exceptions.CantStartJMXException;
18  import cz.cuni.amis.pogamut.base.agent.exceptions.JMXAlreadyEnabledException;
19  import cz.cuni.amis.pogamut.base.agent.jmx.adapter.AgentMBeanAdapter;
20  import cz.cuni.amis.pogamut.base.utils.Pogamut;
21  import cz.cuni.amis.pogamut.base.utils.PogamutProperty;
22  import cz.cuni.amis.pogamut.base.utils.guice.AgentScoped;
23  import cz.cuni.amis.pogamut.base.utils.jmx.PogamutJMX;
24  import cz.cuni.amis.pogamut.base.utils.jmx.PogamutMBeanServer;
25  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
26  import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
27  import cz.cuni.amis.utils.ExceptionToString;
28  
29  /**
30   * Wraps a few methods into one place so it won't plague the public method space
31   * of the agent. (Make things a bit clear...).
32   * <p>
33   * <p>
34   * Contains list of IJMXEnabled components that should be enabled when the whole
35   * JMX feature of the agent is fired up.
36   * <p>
37   * <p>
38   * Note that jmx domain is taken from the java property "pogamut.jmx.domain".
39   * 
40   * @author Jimmy
41   */
42  @AgentScoped
43  public class AgentJMXComponents<T extends IAgent> {
44  
45  	public static final String LOG_CATEGORY_NAME = "AgentJMXComponents";
46  
47  	/**
48  	 * Separates JMX server address and the agent's MBean object name in address
49  	 * exported to the outside.
50  	 */
51  	public static final String JMX_SERVER_AGENT_NAME_DELIM = "|";
52  
53  	/**
54  	 * MBeanServer the JMX is currently using. (If null the jmx is not enabled.)
55  	 */
56  	private PogamutMBeanServer mBeanServer = null;
57  
58  	/**
59  	 * Current domain name of the objects that are registered by this agent and
60  	 * its components.
61  	 * <p>
62  	 * <p>
63  	 * Note that every agent MUST HAVE its own unique domain.
64  	 */
65  	private String jmxDomain = null;
66  
67  	/**
68  	 * List of IJMXEnabled components that are enabled when enableJMX() is
69  	 * called.
70  	 */
71  	private Set<IJMXEnabled> jmxComponents = new LinkedHashSet<IJMXEnabled>();
72  
73  	/**
74  	 * ObjectName of the agent owning this class.
75  	 */
76  	private ObjectName agentJMXName = null;
77  
78  	private IAgentLogger agentLogger;
79  
80  	private LogCategory log;
81  
82  	/**
83  	 * Agent that owns the JMX (equally the agent this object supports).
84  	 */
85  	private T agent;
86  
87  	@Inject
88  	public AgentJMXComponents(T agent) {
89  		this.agent = agent;
90  		this.agentLogger = agent.getLogger();
91  		this.log = agentLogger.getCategory(LOG_CATEGORY_NAME);
92  	}
93  
94  	/**
95  	 * Adding new IJMXEnabled component to the list - registering it so it will
96  	 * be notified when the enableJMX() is called.
97  	 * 
98  	 * @param component
99  	 */
100 	public void addComponent(IJMXEnabled component) {
101 		synchronized (jmxComponents) {			
102 			if (jmxComponents.contains(component)) {
103 				return;
104 			}
105 			if (log.isLoggable(Level.FINER)) log.finer("Adding new JMX component " + component);
106 			jmxComponents.add(component);
107 			if (isJMXEnabled()) {
108 				if (log.isLoggable(Level.FINE)) log.fine("Enabling JMX component " + component);
109 				component.enableJMX(mBeanServer, agentJMXName);
110 			}
111 			if (log.isLoggable(Level.INFO)) log.info("New JMX component added: " + component);
112 		}
113 	}
114 
115 	/**
116 	 * MBeanServer, if null the jmx is not enabled.
117 	 * 
118 	 * @return
119 	 */
120 	public MBeanServer getMBeanServer() {
121 		return mBeanServer;
122 	}
123 
124 	/**
125 	 * JMX domain of the whole agent - used to construct ObjectName instances.
126 	 * If null the jmx is not enabled.
127 	 * 
128 	 * @return
129 	 */
130 	public String getJMXDomain() {
131 		return jmxDomain;
132 	}
133 
134 	/**
135 	 * Whether the JMX is enabled or not.
136 	 * 
137 	 * @return
138 	 */
139 	public boolean isJMXEnabled() {
140 		return jmxDomain != null;
141 	}
142 
143 	/**
144 	 * Returns ObjectName of the agent.
145 	 * 
146 	 * @return
147 	 */
148 	public ObjectName getAgentJMXName() {
149 		return agentJMXName;
150 	}
151 
152 	/**
153 	 * This enables the JMX feature on the whole agent notifying all IJMXEnabled
154 	 * components to register itself to provided mBeanServer.
155 	 * <p>
156 	 * <p>
157 	 * Note that jmxDomain must be well-formed in JMX Object Name sense.
158 	 * 
159 	 * @param mBeanServer
160 	 * @param jmxDomain
161 	 * @return full jmx address of the agent
162 	 * @throws JMXAlreadyEnabledException
163 	 * @throws CantStartJMXException
164 	 */
165 	public String enableJMX() throws JMXAlreadyEnabledException, CantStartJMXException {
166 		synchronized (jmxComponents) {
167 			if (!isJMXEnabled()) {
168 				this.mBeanServer = new PogamutMBeanServer(Pogamut.getPlatform().getMBeanServer());
169 				jmxDomain = PogamutJMX.getPogamutJMXDomain();
170 				try {
171 					agentJMXName = PogamutJMX.getAgentObjectName(agent.getComponentId());
172 				} catch (Exception e) {
173 					throw new CantStartJMXException("Can't create object name for the agent.", e, log, this);
174 				}
175 	
176 				// export the agent itself
177 				try {
178 					// create the MBean for agent
179 					AgentMBeanAdapter agentMBean = createAgentMBean(agentJMXName, mBeanServer);
180 					mBeanServer.registerMBean(agentMBean, agentJMXName);
181 				} catch (Exception ex) {
182 					throw new CantStartJMXException("Agent MBean cannot be registered.", ex, log, this);
183 				}
184 	
185 				if (log.isLoggable(Level.INFO)) log.info("Enabling JMX.");
186 				int numOk = 0;
187 				for (IJMXEnabled jmxComponent : jmxComponents) {
188 					try {
189 						if (log.isLoggable(Level.FINE)) log.fine("Starting JMX component: " + jmxComponent);
190 						jmxComponent.enableJMX(mBeanServer, agentJMXName);
191 						++numOk;
192 					} catch (JMXAlreadyEnabledException e) {
193 						log.log(Level.SEVERE, ExceptionToString.process("IJMXEnabled[class="+ jmxComponent.getClass().getName()+ ",name="+ jmxComponent.toString()+ "]: states that it's been already enabled.", e));
194 					} catch (CantStartJMXException e) {
195 						if (log.isLoggable(Level.SEVERE)) log.severe(ExceptionToString.process("IJMXEnabled[class="+ jmxComponent.getClass().getName() + ",name="+ jmxComponent.toString()+ "]: can't start it's JMX.", e));
196 					}
197 				}
198 				if (log.isLoggable(Level.INFO)) log.info(numOk + " JMX components enabled");
199 			}
200 		}
201 		return Pogamut.getPlatform().getMBeanServerURL().toString() + AgentJMXComponents.JMX_SERVER_AGENT_NAME_DELIM + getAgentJMXName().toString();
202 	}
203 
204 	/**
205 	 * Factory method for creating agent MBean.
206 	 * 
207 	 * @param objectName
208 	 * @param mbs
209 	 * @return
210 	 */
211 	protected AgentMBeanAdapter createAgentMBean(ObjectName objectName,
212 			MBeanServer mbs) throws MalformedObjectNameException,
213 			InstanceAlreadyExistsException, InstanceAlreadyExistsException,
214 			MBeanRegistrationException, NotCompliantMBeanException {
215 		return new AgentMBeanAdapter(agent, objectName, mbs);
216 	}
217 	
218 	/**
219 	 * Unregister all agent's MBeans / Listeners from MBeanServer.
220 	 */
221 	public void unregisterJMX() {
222 		if (isJMXEnabled()) {
223 			if (log.isLoggable(Level.WARNING)) log.warning("Unregistering JMX components.");
224 			mBeanServer.unregisterAll();
225 		}
226 	}
227 	
228 	/**
229 	 * Re-register all agent's MBeans / Listener into the MBeanServer again.
230 	 */
231 	public void registerJMX() {
232 		if (isJMXEnabled()) {
233 			try {
234 				if (log.isLoggable(Level.WARNING)) log.warning("Re-registering JMX components.");
235 				mBeanServer.registerAll();
236 			} catch (Exception e) {
237 				throw new CantStartJMXException("JMX components can't be re-registered: " + e.getMessage(), e, this);
238 			}
239 		}
240 	}
241 	
242 }