1 | /* |
2 | Copyright (C) 2004 MySQL AB |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License version 2 as |
6 | published by the Free Software Foundation. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
16 | |
17 | */ |
18 | package com.mysql.management.jmx; |
19 | |
20 | import java.lang.reflect.Constructor; |
21 | import java.util.ArrayList; |
22 | import java.util.HashMap; |
23 | import java.util.Iterator; |
24 | import java.util.List; |
25 | import java.util.Map; |
26 | import java.util.TreeMap; |
27 | |
28 | import javax.management.Attribute; |
29 | import javax.management.AttributeList; |
30 | import javax.management.AttributeNotFoundException; |
31 | import javax.management.DynamicMBean; |
32 | import javax.management.MBeanAttributeInfo; |
33 | import javax.management.MBeanConstructorInfo; |
34 | import javax.management.MBeanInfo; |
35 | import javax.management.MBeanNotificationInfo; |
36 | import javax.management.MBeanOperationInfo; |
37 | import javax.management.MBeanParameterInfo; |
38 | import javax.management.ReflectionException; |
39 | |
40 | import com.mysql.management.MysqldResource; |
41 | import com.mysql.management.MysqldResourceI; |
42 | import com.mysql.management.util.DefaultsMap; |
43 | import com.mysql.management.util.Exceptions; |
44 | import com.mysql.management.util.Str; |
45 | |
46 | /** |
47 | * MySQL DynamicMBean |
48 | * |
49 | * @author Eric Herman <eric@mysql.com> |
50 | * @version $Id: SimpleMysqldDynamicMBean.java,v 1.1 2005/02/16 21:46:11 eherman |
51 | * Exp $ |
52 | */ |
53 | public class SimpleMysqldDynamicMBean implements DynamicMBean { |
54 | |
55 | public static final String AUTOSTART_ATTR = "autostart"; |
56 | |
57 | public static final String START_METHOD = "startMysqld"; |
58 | |
59 | public static final String STOP_METHOD = "stop"; |
60 | |
61 | private static final String VERSION = "$Id: SimpleMysqldDynamicMBean.java,v 1.15 2005/08/31 01:21:16 eherman Exp $"; |
62 | |
63 | private MysqldResourceI mysqldResource; |
64 | |
65 | private MBeanAttributeInfo[] attrInfos; |
66 | |
67 | private MBeanConstructorInfo[] consInfo; |
68 | |
69 | private List mBeanOperationInfoList; |
70 | |
71 | private MBeanOperationInfo startMysqldOp; |
72 | |
73 | private MBeanOperationInfo stopOp; |
74 | |
75 | private MBeanNotificationInfo[] notesInfos; |
76 | |
77 | private MBeanInfo mbeanInfo; |
78 | |
79 | private DefaultsMap attributes; |
80 | |
81 | private Str str; |
82 | |
83 | Object[] lastInvocation; |
84 | |
85 | /** |
86 | * Directs output generated by MySQL to Standard Out and Standard Error. |
87 | * This is the constructor included in the MBeanInfo. |
88 | */ |
89 | public SimpleMysqldDynamicMBean() { |
90 | this(new MysqldResource()); |
91 | } |
92 | |
93 | /** |
94 | * This constructor is useful for tests which need to have "stub" or "mock" |
95 | * implementations of the underlying MySQL resource. This constructor is not |
96 | * included int the MBeanInfo. |
97 | */ |
98 | protected SimpleMysqldDynamicMBean(MysqldResourceI mysqldResource) { |
99 | this.mysqldResource = mysqldResource; |
100 | this.str = new Str(); |
101 | this.notesInfos = new MBeanNotificationInfo[0]; |
102 | this.mBeanOperationInfoList = new ArrayList(); |
103 | this.startMysqldOp = newVoidMBeanOperation(START_METHOD, "Start MySQL"); |
104 | this.stopOp = newVoidMBeanOperation(STOP_METHOD, "Stop MySQL"); |
105 | getMBeanOperationInfoList().add(startMysqldOp); |
106 | initAttributes(); |
107 | initConstructors(); |
108 | } |
109 | |
110 | protected List getMBeanOperationInfoList() { |
111 | return mBeanOperationInfoList; |
112 | } |
113 | |
114 | private void initConstructors() { |
115 | Constructor[] constructors = this.getClass().getConstructors(); |
116 | Constructor con = null; |
117 | for (int i = 0; con == null && i < constructors.length; i++) { |
118 | if (constructors[i].getParameterTypes().length == 0) |
119 | con = constructors[i]; |
120 | } |
121 | consInfo = new MBeanConstructorInfo[1]; |
122 | consInfo[0] = new MBeanConstructorInfo("MySQL", con); |
123 | } |
124 | |
125 | private void initAttributes() { |
126 | attributes = new DefaultsMap(); |
127 | Map options = new TreeMap(mysqldResource.getServerOptions()); |
128 | List attInfs = new ArrayList(); |
129 | makeAttribute(attInfs, AUTOSTART_ATTR, Boolean.FALSE.toString(), true); |
130 | for (Iterator iter = options.entrySet().iterator(); iter.hasNext();) { |
131 | Map.Entry entry = (Map.Entry) iter.next(); |
132 | String attName = (String) entry.getKey(); |
133 | String attValue = (String) entry.getValue(); |
134 | makeAttribute(attInfs, attName, attValue, true); |
135 | } |
136 | makeAttribute(attInfs, versionAttributeName(), VERSION, false); |
137 | this.attrInfos = (MBeanAttributeInfo[]) attInfs |
138 | .toArray(new MBeanAttributeInfo[attInfs.size()]); |
139 | } |
140 | |
141 | private void makeAttribute(List attInfs, String attName, String attValue, |
142 | boolean isWritable) { |
143 | Attribute attr = new Attribute(attName, attValue); |
144 | String description = ""; |
145 | MBeanAttributeInfo atInfo = new MBeanAttributeInfo(attName, |
146 | String.class.getName(), description, true, isWritable, false); |
147 | |
148 | attributes.put(attName, attr); |
149 | attInfs.add(atInfo); |
150 | } |
151 | |
152 | /** |
153 | * @param attributeName |
154 | * represents a command line argument to a MySQL database. |
155 | * @return a String with the value associated the argument |
156 | */ |
157 | public synchronized Object getAttribute(String attributeName) |
158 | throws AttributeNotFoundException { |
159 | Attribute attribute = ((Attribute) attributes.get(attributeName)); |
160 | if (attribute == null) { |
161 | throw new AttributeNotFoundException(attributeName); |
162 | } |
163 | return attribute.getValue(); |
164 | } |
165 | |
166 | /** |
167 | * Given an array of command line option names, an "AttributeList" is |
168 | * created containing those option names and the values for each. Called by |
169 | * a JMX Agent |
170 | */ |
171 | public synchronized AttributeList getAttributes(String[] attributeNames) { |
172 | AttributeList list = new AttributeList(attributeNames.length); |
173 | for (int i = 0; i < attributeNames.length; i++) { |
174 | Attribute att = (Attribute) attributes.get(attributeNames[i]); |
175 | list.add(att); |
176 | } |
177 | return list; |
178 | } |
179 | |
180 | /** |
181 | * Returns an MBeanInfo object knowing the class name, what attributes are |
182 | * available, the constructor information, and either the start or stop |
183 | * method information. |
184 | */ |
185 | public synchronized MBeanInfo getMBeanInfo() { |
186 | if (mbeanInfo == null) { |
187 | mbeanInfo = new MBeanInfo(this.getClass().getName(), "MySQL MBean", |
188 | attrInfos, consInfo, opsInfo(), notesInfos); |
189 | } |
190 | return mbeanInfo; |
191 | } |
192 | |
193 | private MBeanOperationInfo[] opsInfo() { |
194 | int length = getMBeanOperationInfoList().size(); |
195 | MBeanOperationInfo[] array = new MBeanOperationInfo[length]; |
196 | return (MBeanOperationInfo[]) getMBeanOperationInfoList() |
197 | .toArray(array); |
198 | } |
199 | |
200 | /** |
201 | * Allows the JMX Agent to pass along calls of "startMysqld" or |
202 | * "startMysqld" with no args of any type |
203 | */ |
204 | public synchronized Object invoke(String methodName, Object args[], |
205 | String types[]) throws ReflectionException { |
206 | lastInvocation = new Object[] { methodName, args, types }; |
207 | |
208 | clearMBeanInfo(); |
209 | |
210 | if (methodName.equals(START_METHOD)) { |
211 | mysqldResource.start("MysqldMBean", attributesToOpionMap()); |
212 | getMBeanOperationInfoList().remove(startMysqldOp); |
213 | getMBeanOperationInfoList().add(stopOp); |
214 | freezeAttributes(); |
215 | return null; |
216 | } |
217 | if (methodName.equals(STOP_METHOD)) { |
218 | mysqldResource.shutdown(); |
219 | getMBeanOperationInfoList().remove(stopOp); |
220 | getMBeanOperationInfoList().add(startMysqldOp); |
221 | initAttributes(); |
222 | return null; |
223 | } |
224 | |
225 | String msg = methodName + " not implemented"; |
226 | throw new ReflectionException(new NoSuchMethodException(msg), msg); |
227 | } |
228 | |
229 | protected void clearMBeanInfo() { |
230 | this.mbeanInfo = null; |
231 | } |
232 | |
233 | void freezeAttributes() { |
234 | boolean isWritable = false; |
235 | for (int i = 0; i < attrInfos.length; i++) { |
236 | MBeanAttributeInfo info = attrInfos[i]; |
237 | attrInfos[i] = new MBeanAttributeInfo(info.getName(), info |
238 | .getType(), info.getDescription(), info.isReadable(), |
239 | isWritable, info.isIs()); |
240 | } |
241 | } |
242 | |
243 | Map attributesToOpionMap() { |
244 | Map options = new HashMap(); |
245 | for (Iterator iter = attributes.getChanged().values().iterator(); iter |
246 | .hasNext();) { |
247 | Attribute at = (Attribute) iter.next(); |
248 | options.put(at.getName(), at.getValue()); |
249 | } |
250 | options.remove(AUTOSTART_ATTR); |
251 | return options; |
252 | } |
253 | |
254 | /** |
255 | * Allows the JMX Agent to set a command line option to be used when MySQL |
256 | * is launched. |
257 | * |
258 | * @throws AttributeNotFoundException |
259 | * ReflectionException |
260 | */ |
261 | public synchronized void setAttribute(Attribute attribute) |
262 | throws AttributeNotFoundException { |
263 | |
264 | String name = attribute.getName(); |
265 | if (!attributes.containsKey(name)) { |
266 | // new RuntimeException("attempting to set '" + name + "'") |
267 | // .printStackTrace(); |
268 | throw new AttributeNotFoundException(name); |
269 | } |
270 | attributes.put(name, attribute); |
271 | } |
272 | |
273 | /** |
274 | * Allows the JMX Agent to set a number of command line options at once. |
275 | */ |
276 | public synchronized AttributeList setAttributes(AttributeList attributes) { |
277 | for (int i = 0; i < attributes.size(); i++) { |
278 | final Attribute att = (Attribute) attributes.get(i); |
279 | new Exceptions.VoidBlock() { |
280 | public void inner() throws Exception { |
281 | setAttribute(att); |
282 | } |
283 | }.exec(); |
284 | } |
285 | return attributes; |
286 | } |
287 | |
288 | MysqldResourceI getMysqldResource() { |
289 | return mysqldResource; |
290 | } |
291 | |
292 | String versionAttributeName() { |
293 | return "version_of_" + str.shortClassName(getClass()); |
294 | } |
295 | |
296 | protected MBeanOperationInfo newVoidMBeanOperation(String method, |
297 | String description) { |
298 | return new MBeanOperationInfo(method, description, |
299 | new MBeanParameterInfo[0], "void", MBeanOperationInfo.ACTION); |
300 | } |
301 | } |