1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2006, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.system.microcontainer;
23
24 import java.lang.reflect.InvocationHandler;
25 import java.lang.reflect.Method;
26 import java.lang.reflect.Proxy;
27 import java.util.HashMap;
28
29 import javax.management.MBeanInfo;
30 import javax.management.MBeanOperationInfo;
31 import javax.management.MBeanServer;
32 import javax.management.ObjectName;
33
34 import org.jboss.mx.util.JMXExceptionDecoder;
35 import org.jboss.system.Service;
36 import org.jboss.system.ServiceController;
37
38 /**
39 * An implementation of InvocationHandler used to proxy of the Service
40 * interface for mbeans. It determines which of the start/stop
41 * methods of the Service interface an mbean implements by inspecting its
42 * MBeanOperationInfo values. Each Service interface method that has a
43 * matching operation is forwarded to the mbean by invoking the method
44 * through the MBeanServer object.<p>
45 *
46 * This class is based on the old ServiceConfigurator
47 *
48 * @author <a href="mailto:marc@jboss.org">Marc Fleury</a>
49 * @author <a href="mailto:hiram@jboss.org">Hiram Chirino</a>
50 * @author <a href="mailto:d_jencks@users.sourceforge.net">David Jencks</a>
51 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
52 * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
53 * @author <a href="adrian@jboss.com">Adrian Brock</a>
54 * @version $Revision: 1.1 $
55 */
56 public class ServiceProxy implements InvocationHandler
57 {
58 /**
59 * A mapping from the Service interface method names to the corresponding
60 * index into the ServiceProxy.hasOp array.
61 */
62 private static HashMap<String, Integer> serviceOpMap = new HashMap<String, Integer>();
63
64 // A singleton proxy with no callouts
65 private static Service NO_LIFECYCLE_CALLOUT;
66
67 /**
68 * Initialize the service operation map.
69 */
70 static
71 {
72 serviceOpMap.put("create", new Integer(0));
73 serviceOpMap.put("start", new Integer(1));
74 serviceOpMap.put("destroy", new Integer(2));
75 serviceOpMap.put("stop", new Integer(3));
76 Class<?>[] interfaces = { Service.class };
77 NO_LIFECYCLE_CALLOUT = (Service) Proxy.newProxyInstance(Service.class.getClassLoader(), interfaces, NoLifecycleCallout.INSTANCE);
78 }
79
80 private boolean[] hasOp = {false, false, false, false};
81 private ObjectName objectName;
82 private MBeanServer server;
83
84 /** Whether we have the lifecycle method */
85 private boolean hasJBossInternalLifecycle;
86
87 /**
88 * Get the Service interface through which the mbean given by objectName will be managed.
89 *
90 * @param objectName the object name
91 * @param server the mbean server
92 * @return The Service value
93 * @throws Exception for any error
94 */
95 public static Service getServiceProxy(ObjectName objectName, MBeanServer server) throws Exception
96 {
97 return getServiceProxy(objectName, server, true);
98 }
99
100 /**
101 * Get the Service interface through which the mbean given by objectName will be managed.
102 *
103 * @param objectName the object name
104 * @param server the mbean server
105 * @param includeLifecycle include lifecycle
106 * @return The Service value
107 * @throws Exception for any error
108 */
109 public static Service getServiceProxy(ObjectName objectName, MBeanServer server, boolean includeLifecycle) throws Exception
110 {
111 Service service = null;
112 MBeanInfo info = server.getMBeanInfo(objectName);
113 MBeanOperationInfo[] opInfo = info.getOperations();
114 Class<?>[] interfaces = { Service.class };
115 InvocationHandler handler = new ServiceProxy(objectName, server, opInfo);
116 if (includeLifecycle == false)
117 service = NO_LIFECYCLE_CALLOUT;
118 else
119 service = (Service) Proxy.newProxyInstance(Service.class.getClassLoader(), interfaces, handler);
120
121 return service;
122 }
123
124 /**
125 * Go through the opInfo array and for each operation that matches on of
126 * the Service interface methods set the corresponding hasOp array value
127 * to true.
128 *
129 * @param objectName the object name
130 * @param server the mbean server
131 * @param opInfo the MBean operation info
132 */
133 public ServiceProxy(ObjectName objectName, MBeanServer server, MBeanOperationInfo[] opInfo)
134 {
135 this.server = server;
136 this.objectName = objectName;
137
138 for (int op = 0; op < opInfo.length; op++)
139 {
140 MBeanOperationInfo info = opInfo[op];
141 String name = info.getName();
142
143 if (name.equals(ServiceController.JBOSS_INTERNAL_LIFECYCLE))
144 {
145 hasJBossInternalLifecycle = true;
146 continue;
147 }
148
149 Integer opID = serviceOpMap.get(name);
150 if (opID == null)
151 {
152 continue;
153 }
154
155 // Validate that is a no-arg void return type method
156 if (info.getReturnType().equals("void") == false)
157 {
158 continue;
159 }
160 if (info.getSignature().length != 0)
161 {
162 continue;
163 }
164
165 hasOp[opID.intValue()] = true;
166 }
167 }
168
169 /**
170 * Map the method name to a Service interface method index and if the
171 * corresponding hasOp array element is true, dispatch the method to the
172 * mbean we are proxying.
173 *
174 * @param proxy
175 * @param method
176 * @param args
177 * @return Always null.
178 * @throws Throwable
179 */
180 public Object invoke(Object proxy, Method method, Object[] args)
181 throws Throwable
182 {
183 String name = method.getName();
184
185 if (hasJBossInternalLifecycle)
186 {
187 try
188 {
189 server.invoke(objectName, ServiceController.JBOSS_INTERNAL_LIFECYCLE, new Object[] { name }, ServiceController.JBOSS_INTERNAL_LIFECYCLE_SIG);
190 return null;
191 }
192 catch (Exception e)
193 {
194 throw JMXExceptionDecoder.decode(e);
195 }
196 }
197
198 Integer opID = serviceOpMap.get(name);
199
200 if (opID != null && hasOp[opID.intValue()] == true)
201 {
202 // deal with those pesky JMX exceptions
203 try
204 {
205 String[] sig = {};
206 server.invoke(objectName, name, args, sig);
207 }
208 catch (Exception e)
209 {
210 throw JMXExceptionDecoder.decode(e);
211 }
212 }
213
214 return null;
215 }
216
217 private static class NoLifecycleCallout implements InvocationHandler
218 {
219 private static NoLifecycleCallout INSTANCE = new NoLifecycleCallout();
220
221 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
222 {
223 return null;
224 }
225 }
226 }