1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, 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.mx.server;
23
24 import java.lang.reflect.Method;
25 import java.lang.reflect.Modifier;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Map;
31
32 import javax.management.Attribute;
33 import javax.management.AttributeList;
34 import javax.management.AttributeNotFoundException;
35 import javax.management.Descriptor;
36 import javax.management.InvalidAttributeValueException;
37 import javax.management.JMRuntimeException;
38 import javax.management.ListenerNotFoundException;
39 import javax.management.MBeanAttributeInfo;
40 import javax.management.MBeanException;
41 import javax.management.MBeanInfo;
42 import javax.management.MBeanNotificationInfo;
43 import javax.management.MBeanOperationInfo;
44 import javax.management.MBeanParameterInfo;
45 import javax.management.MBeanRegistration;
46 import javax.management.MBeanServer;
47 import javax.management.NotificationBroadcaster;
48 import javax.management.NotificationEmitter;
49 import javax.management.NotificationFilter;
50 import javax.management.NotificationListener;
51 import javax.management.ObjectName;
52 import javax.management.ReflectionException;
53 import javax.management.RuntimeErrorException;
54 import javax.management.RuntimeMBeanException;
55 import javax.management.RuntimeOperationsException;
56 import javax.management.modelmbean.ModelMBeanInfo;
57 import javax.management.modelmbean.ModelMBeanInfoSupport;
58
59 import org.jboss.logging.Logger;
60 import org.jboss.mx.interceptor.AttributeDispatcher;
61 import org.jboss.mx.interceptor.Interceptor;
62 import org.jboss.mx.interceptor.ReflectedDispatcher;
63 import org.jboss.mx.metadata.StandardMetaData;
64 import org.jboss.mx.modelmbean.ModelMBeanConstants;
65 import org.jboss.mx.server.InvocationContext.NullDispatcher;
66 import org.jboss.mx.server.registry.MBeanEntry;
67 import org.jboss.util.Strings;
68
69 /**
70 * A base MBeanInvoker class that provides common state
71 *
72 * @author <a href="mailto:juha@jboss.org">Juha Lindfors</a>.
73 * @author <a href="mailto:scott.stark@jboss.org">Scott Stark</a>.
74 * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>.
75 * @version $Revision: 43658 $
76 */
77 public abstract class AbstractMBeanInvoker
78 implements MBeanInvoker
79 {
80 /**
81 * Used to propagate the MBeanEntry during the preRegister callback
82 */
83 static ThreadLocal preRegisterInfo = new ThreadLocal();
84
85 // Attributes ----------------------------------------------------
86
87 /**
88 * The target object for this invoker.
89 */
90 private Object resource = null;
91 /**
92 * The mbean server register entry used for the TCL
93 */
94 protected MBeanEntry resourceEntry = null;
95
96 /**
97 * Whether this is a dynamic resource
98 */
99 protected boolean dynamicResource = true;
100
101 /**
102 * The metadata describing this MBean.
103 */
104 protected MBeanInfo info = null;
105
106 protected Map attributeContextMap = new HashMap();
107 protected Map operationContextMap = new HashMap();
108 protected Map constructorContextMap = new HashMap();
109
110 protected InvocationContext getMBeanInfoCtx = null;
111 protected InvocationContext preRegisterCtx = null;
112 protected InvocationContext postRegisterCtx = null;
113 protected InvocationContext preDeregisterCtx = null;
114 protected InvocationContext postDeregisterCtx = null;
115
116 // TODO: allow to config invoker specific logs
117 // : multitarget mbean for invoker + log?
118
119 protected Logger log = Logger.getLogger(AbstractMBeanInvoker.class);
120
121 /**
122 * The MBeanServer passed in to preRegister
123 */
124
125 private MBeanServer server;
126
127 /**
128 * Set the MBeanEntry thread local value.
129 * @param entry - the entry that will be used on successful registration
130 */
131 public static void setMBeanEntry(MBeanEntry entry)
132 {
133 preRegisterInfo.set(entry);
134 }
135
136 /**
137 * An accessor for the MBeanEntry thread local
138 * @return
139 */
140 public static MBeanEntry getMBeanEntry()
141 {
142 return (MBeanEntry) preRegisterInfo.get();
143 }
144 // Constructors --------------------------------------------------
145
146 /**
147 * Constructs a new invoker.
148 */
149 public AbstractMBeanInvoker()
150 {
151 }
152
153 /**
154 * Constructs a new invoker with a given target resource.
155 */
156 public AbstractMBeanInvoker(Object resource)
157 {
158 this.resource = resource;
159 }
160
161 /**
162 * Constructs an invoker with the target resource entry.
163 * @param resourceEntry
164 */
165 public AbstractMBeanInvoker(MBeanEntry resourceEntry)
166 {
167 this.resourceEntry = resourceEntry;
168 this.resource = resourceEntry.getResourceInstance();
169 }
170
171 // DynamicMBean implementation -----------------------------------
172
173 /**
174 * Invokes the target resource. The default invocation used by this invoker
175 * implement sends the invocation through a stack of interceptors before
176 * reaching the target method.
177 * @param operationName name of the target method
178 * @param args argumetns for the target method
179 * @param signature signature of the target method
180 * @throws MBeanException if the target method raised a hecked exception
181 * @throws ReflectionException if there was an error trying to resolve or
182 * invoke the target method
183 * @throws RuntimeMBeanException if the target method raised an unchecked
184 * exception
185 */
186 public Object invoke(String operationName, Object[] args, String[] signature)
187 throws MBeanException, ReflectionException
188 {
189
190 // TODO: __JBOSSMX_INVOCATION
191
192 if (operationName == null)
193 throw new ReflectionException(new IllegalArgumentException("Null operation name"));
194
195 // If we have dynamic capability, check for a dynamic invocation
196 String opName = operationName;
197 if (dynamicResource)
198 {
199 int dot = operationName.lastIndexOf('.');
200 if (dot != -1)
201 {
202 if (dot < operationName.length() - 1)
203 opName = operationName.substring(dot + 1);
204 }
205 }
206
207 // get the server side invocation context
208 OperationKey key = new OperationKey(opName, signature);
209 InvocationContext ctx = (InvocationContext) operationContextMap.get(key);
210
211 // if the server does not contain this context, we do not have the operation
212 if (ctx == null)
213 {
214 // This is just stupid - the RI is fundamentally broken and hence the spec
215 boolean operationExists = false;
216 if (dynamicResource)
217 {
218 for (Iterator i = operationContextMap.keySet().iterator(); i.hasNext();)
219 {
220 OperationKey thisKey = (OperationKey) i.next();
221 if (opName.equals(thisKey.keys[0]))
222 {
223 operationExists = true;
224 break;
225 }
226 }
227 if (operationExists)
228 throw new ReflectionException(new NoSuchMethodException("Unable to find operation " + operationName +
229 getSignatureString(signature)));
230 }
231 throw new ReflectionException(new IllegalArgumentException("Unable to find operation " + operationName +
232 getSignatureString(signature)));
233 }
234
235 // create the invocation object
236 Invocation invocation = new Invocation();
237
238 // copy the server's invocation context to the invocation
239 invocation.addContext(ctx);
240
241 // set the invocation's entry point
242 invocation.setType(InvocationContext.OP_INVOKE);
243
244 // Use the passed operation
245 invocation.setName(operationName);
246
247 // set the args
248 invocation.setArgs(args);
249
250 override(invocation);
251
252 ClassLoader mbeanTCL = resourceEntry.getClassLoader();
253 final ClassLoader ccl = TCLAction.UTIL.getContextClassLoader();
254 boolean setCl = ccl != mbeanTCL && mbeanTCL != null;
255 if (setCl)
256 {
257 TCLAction.UTIL.setContextClassLoader(mbeanTCL);
258 }
259
260 try
261 {
262 // the default invocation implementation will invoke each interceptor
263 // declared in the invocation context before invoking the target method
264 return invocation.invoke();
265 }
266 catch (MBeanException e)
267 {
268 throw e;
269 }
270 catch (ReflectionException e)
271 {
272 throw e;
273 }
274 catch (JMRuntimeException e)
275 {
276 throw e;
277 }
278 catch (Throwable t)
279 {
280 rethrowAsMBeanException(t);
281 return null;
282 }
283
284 // TODO: should be fixed by adding invocation return value object
285 finally
286 {
287 Descriptor descriptor = invocation.getDescriptor();
288 if (descriptor != null)
289 {
290 ctx.setDescriptor(descriptor);
291 if (dynamicResource && ModelMBeanConstants.OPERATION_DESCRIPTOR.equals(descriptor.getFieldValue(ModelMBeanConstants.DESCRIPTOR_TYPE)))
292 {
293 ModelMBeanInfoSupport minfo = (ModelMBeanInfoSupport) info;
294 minfo.setDescriptor(descriptor, ModelMBeanConstants.OPERATION_DESCRIPTOR);
295 }
296 }
297 invocation.setArgs(null);
298 invocation.setDescriptor(null);
299 invocation.setDispatcher(null);
300
301 if (setCl)
302 {
303 TCLAction.UTIL.setContextClassLoader(ccl);
304 }
305 }
306
307 }
308
309 /**
310 * Returns an attribte value. The request for the value is forced through a
311 * set of interceptors before the value is returned.
312 * @param attribute attribute name
313 * @return attribute value
314 * @throws AttributeNotFoundException if the requested attribute is not part
315 * of the MBean's management interface
316 * @throws MBeanException if retrieving the attribute value causes an
317 * application exception
318 * @throws ReflectionException if there was an error trying to retrieve the
319 * attribute value
320 */
321 public Object getAttribute(String attribute)
322 throws AttributeNotFoundException, MBeanException, ReflectionException
323 {
324 // TODO: __JBOSSMX_INVOCATION
325
326 if (attribute == null)
327 throw new RuntimeOperationsException(new IllegalArgumentException("Cannot get null attribute"));
328
329 // lookup the server side invocation context
330 InvocationContext ctx = (InvocationContext) attributeContextMap.get(attribute);
331
332 // if we don't have a server side invocation context for the attribute,
333 // it does not exist as far as we are concerned
334 if (ctx == null)
335 throw new AttributeNotFoundException("not found: " + attribute);
336
337 if (ctx.isReadable() == false)
338 throw new AttributeNotFoundException("Attribute '" + attribute + "' found, but it is not readable");
339
340 // create the invocation object
341 Invocation invocation = new Invocation();
342
343 // copy the server's invocation context to the invocation
344 invocation.addContext(ctx);
345
346 // indicate the invocation access point was getAttribute() method
347 invocation.setType(InvocationContext.OP_GETATTRIBUTE);
348 invocation.setArgs(null);
349
350 override(invocation);
351
352 ClassLoader mbeanTCL = resourceEntry.getClassLoader();
353 final ClassLoader ccl = TCLAction.UTIL.getContextClassLoader();
354 boolean setCl = ccl != mbeanTCL && mbeanTCL != null;
355 if (setCl)
356 {
357 TCLAction.UTIL.setContextClassLoader(mbeanTCL);
358 }
359
360 try
361 {
362 return invocation.invoke();
363 }
364 catch (AttributeNotFoundException e)
365 {
366 throw e;
367 }
368 catch (MBeanException e)
369 {
370 throw e;
371 }
372 catch (ReflectionException e)
373 {
374 throw e;
375 }
376 catch (JMRuntimeException e)
377 {
378 throw e;
379 }
380 catch (Throwable t)
381 {
382 rethrowAsMBeanException(t);
383 return null;
384 }
385
386 // TODO: should be fixed by adding invocation return value object
387 finally
388 {
389 Descriptor attrDesc = invocation.getDescriptor();
390 ctx.setDescriptor(attrDesc);
391 updateAttributeInfo(attrDesc);
392
393 if (setCl)
394 {
395 TCLAction.UTIL.setContextClassLoader(ccl);
396 }
397 }
398 }
399
400 /**
401 * Sets an attribute value. The operation is forced through a set of
402 * interceptors before the new value for the attribute is set.
403 * @param attribute new attribute value
404 * @throws AttributeNotFoundException if the requested attribute is not part
405 * of the MBean's management interface
406 * @throws InvalidAttributeValueException if the attribute contains a value
407 * not suitable for the attribute
408 * @throws MBeanException if setting the attribute value causes an
409 * application exception
410 * @throws ReflectionException if there was an error trying to set the
411 * attribute value.
412 */
413 public void setAttribute(Attribute attribute) throws AttributeNotFoundException,
414 InvalidAttributeValueException, MBeanException, ReflectionException
415 {
416 // TODO: __JBOSSMX_INVOCATION
417
418 if (attribute == null)
419 throw new InvalidAttributeValueException("null attribute");
420
421 // lookup the server side invocation context
422 String name = attribute.getName();
423 InvocationContext ctx = (InvocationContext) attributeContextMap.get(name);
424
425 // if we don't have a server side invocation context for the attribute,
426 // it does not exist as far as we are concerned
427 if (ctx == null)
428 throw new AttributeNotFoundException("not found: " + name);
429 else if (ctx.isWritable() == false)
430 {
431 throw new AttributeNotFoundException("Attribute '" + name
432 + "' is not writable");
433 }
434
435 // create the invocation object
436 Invocation invocation = new Invocation();
437
438 // copy the server context to the invocation
439 invocation.addContext(ctx);
440
441 // indicate the access point as setAttribute()
442 invocation.setType(InvocationContext.OP_SETATTRIBUTE);
443
444 // set the attribute value as the argument
445 invocation.setArgs(new Object[]{attribute.getValue()});
446
447 override(invocation);
448
449 ClassLoader mbeanTCL = resourceEntry.getClassLoader();
450 final ClassLoader ccl = TCLAction.UTIL.getContextClassLoader();
451 boolean setCl = ccl != mbeanTCL && mbeanTCL != null;
452 if (setCl)
453 {
454 TCLAction.UTIL.setContextClassLoader(mbeanTCL);
455 }
456
457 try
458 {
459 // the default invocation implementation will invoke each interceptor
460 // declared in the invocation context before invoking the target method
461 invocation.invoke();
462 }
463 catch (AttributeNotFoundException e)
464 {
465 throw e;
466 }
467 catch (InvalidAttributeValueException e)
468 {
469 throw e;
470 }
471 catch (MBeanException e)
472 {
473 throw e;
474 }
475 catch (ReflectionException e)
476 {
477 throw e;
478 }
479 catch (JMRuntimeException e)
480 {
481 throw e;
482 }
483 catch (Throwable t)
484 {
485 rethrowAsMBeanException(t);
486 }
487
488 // TODO: should be fixed by adding invocation return value object
489 finally
490 {
491 /* Obtain the updated attribute descriptor and propagate to the
492 invocation context and ModelMBeanInfo. The latter is required in
493 order for getMBeanInfo() to show an updated view.
494 */
495 Descriptor attrDesc = invocation.getDescriptor();
496 ctx.setDescriptor(attrDesc);
497 updateAttributeInfo(attrDesc);
498
499 if (setCl)
500 {
501 TCLAction.UTIL.setContextClassLoader(ccl);
502 }
503 }
504 }
505
506 public MBeanInfo getMBeanInfo()
507 {
508 // create the invocation object
509 Invocation invocation = new Invocation(getMBeanInfoCtx);
510
511 // set the invocation's access point as getMBeanInfo()
512 invocation.setType(InvocationContext.OP_GETMBEANINFO);
513
514 if (resourceEntry == null)
515 resourceEntry = getMBeanEntry();
516 ClassLoader mbeanTCL = resourceEntry.getClassLoader();
517 final ClassLoader ccl = TCLAction.UTIL.getContextClassLoader();
518 boolean setCl = ccl != mbeanTCL && mbeanTCL != null;
519 if (setCl)
520 {
521 TCLAction.UTIL.setContextClassLoader(mbeanTCL);
522 }
523
524 try
525 {
526 MBeanInfo info = (MBeanInfo) invocation.invoke();
527 return info;
528 }
529 catch (JMRuntimeException e)
530 {
531 throw e;
532 }
533 catch (Throwable t)
534 {
535 rethrowAsRuntimeMBeanException(t);
536 return null;
537 }
538 finally
539 {
540 if (setCl)
541 {
542 TCLAction.UTIL.setContextClassLoader(ccl);
543 }
544 }
545 }
546
547 public AttributeList getAttributes(java.lang.String[] attributes)
548 {
549 if (attributes == null)
550 throw new IllegalArgumentException("null array");
551
552 AttributeList list = new AttributeList();
553
554 for (int i = 0; i < attributes.length; ++i)
555 {
556 try
557 {
558 list.add(new Attribute(attributes[i], getAttribute(attributes[i])));
559 }
560 catch (Throwable ignored)
561 {
562 // if the attribute could not be retrieved, skip it
563 }
564 }
565
566 return list;
567 }
568
569 public AttributeList setAttributes(AttributeList attributes)
570 {
571 if (attributes == null)
572 throw new IllegalArgumentException("null list");
573
574 AttributeList results = new AttributeList();
575 Iterator it = attributes.iterator();
576
577 while (it.hasNext())
578 {
579 Attribute attr = (Attribute) it.next();
580 try
581 {
582 setAttribute(attr);
583 results.add(attr);
584 }
585 catch (Throwable ignored)
586 {
587 // if unable to set the attribute, skip it
588 if (log.isTraceEnabled())
589 log.trace("Unhandled setAttribute() for attribute: " + attr.getName(), ignored);
590 }
591 }
592
593 return results;
594 }
595
596
597 // MBeanRegistration implementation ------------------------------
598
599 /**
600 * Initializes this invoker. At the registration time we can be sure that all
601 * of the metadata is available and initialize the invoker and cache the data
602 * accordingly. <p>
603 *
604 * Subclasses that override the <tt>preRegister</tt> method must make sure
605 * they call <tt>super.preRegister()</tt> in their implementation to ensure
606 * proper initialization of the invoker.
607 */
608 public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception
609 {
610 this.resourceEntry = (MBeanEntry) preRegisterInfo.get();
611 this.server = server;
612
613 ObjectName mbeanName = null;
614 Descriptor mbeanDescriptor = null;
615 if( info instanceof ModelMBeanInfo )
616 {
617 ModelMBeanInfo minfo = (ModelMBeanInfo) info;
618 try
619 {
620 mbeanDescriptor = minfo.getDescriptor("",
621 ModelMBeanConstants.MBEAN_DESCRIPTOR);
622 String type = (String) mbeanDescriptor.getFieldValue(
623 ModelMBeanConstants.MBEAN_SERVER_INJECTION_TYPE);
624 if( type != null )
625 {
626 inject(ModelMBeanConstants.MBEAN_SERVER_INJECTION_TYPE,
627 type, MBeanServer.class, getServer());
628 }
629 }
630 catch (MBeanException e)
631 {
632 log.warn("Failed to obtain descriptor: "+ModelMBeanConstants.MBEAN_DESCRIPTOR, e);
633 }
634
635 }
636
637 ClassLoader mbeanTCL = resourceEntry.getClassLoader();
638 final ClassLoader ccl = TCLAction.UTIL.getContextClassLoader();
639 boolean setCl = ccl != mbeanTCL && mbeanTCL != null;
640 if (setCl)
641 {
642 TCLAction.UTIL.setContextClassLoader(mbeanTCL);
643 }
644
645 try
646 {
647 initAttributeContexts(info.getAttributes());
648
649 initOperationContexts(info.getOperations());
650
651 if (resource != null)
652 initDispatchers();
653
654 mbeanName = invokePreRegister(server, name);
655 if( mbeanDescriptor != null )
656 {
657 Object value = mbeanDescriptor.getFieldValue(
658 ModelMBeanConstants.OBJECT_NAME_INJECTION_TYPE);
659 String type = (String) value;
660 if( type != null )
661 {
662 inject(ModelMBeanConstants.OBJECT_NAME_INJECTION_TYPE,
663 type, ObjectName.class, mbeanName);
664 }
665 }
666 }
667 finally
668 {
669 if (setCl)
670 {
671 TCLAction.UTIL.setContextClassLoader(ccl);
672 }
673 }
674 return mbeanName;
675 }
676
677 /**
678 */
679 public void postRegister(Boolean registrationSuccessful)
680 {
681 invokePostRegister(registrationSuccessful);
682 }
683
684 /**
685 */
686 public void preDeregister() throws Exception
687 {
688 invokePreDeregister();
689 }
690
691 /**
692 */
693 public void postDeregister()
694 {
695 invokePostDeregister();
696 this.server = null;
697 }
698
699
700 // NotificationEmitter implementation ------------------------
701
702 public void addNotificationListener(NotificationListener listener,
703 NotificationFilter filter, Object handback)
704 {
705 addNotificationListenerToResource(listener, filter, handback);
706 }
707
708 protected void addNotificationListenerToResource(NotificationListener listener, NotificationFilter filter, Object handback)
709 {
710 if (resource instanceof NotificationBroadcaster)
711 {
712 ((NotificationBroadcaster) resource).addNotificationListener(listener, filter, handback);
713 }
714 else
715 {
716 throw new RuntimeMBeanException(new IllegalArgumentException("Target XXX is not a notification broadcaster"
717
718 // FIXME: add the XXX object name, store from registration
719 ));
720 }
721 }
722
723 public void removeNotificationListener(NotificationListener listener)
724 throws ListenerNotFoundException
725 {
726 removeNotificationListenerFromResource(listener);
727 }
728
729 protected void removeNotificationListenerFromResource(NotificationListener listener)
730 throws ListenerNotFoundException
731 {
732 if (resource instanceof NotificationBroadcaster)
733 {
734 ((NotificationBroadcaster) resource).removeNotificationListener(listener);
735 }
736 else
737 {
738 throw new RuntimeMBeanException(new IllegalArgumentException("Target XXX is not a notification broadcaster"
739
740 // FIXME: add the XXX object name, store from registration
741 ));
742 }
743 }
744
745 public void removeNotificationListener(NotificationListener listener,
746 NotificationFilter filter,
747 Object handback)
748 throws ListenerNotFoundException
749 {
750 removeNotificationListenerFromResource(listener, filter, handback);
751 }
752
753 protected void removeNotificationListenerFromResource(NotificationListener listener,
754 NotificationFilter filter,
755 Object handback)
756 throws ListenerNotFoundException
757 {
758 if (resource instanceof NotificationEmitter)
759 {
760 ((NotificationEmitter) resource).removeNotificationListener(listener, filter, handback);
761 }
762 else if (resource instanceof NotificationBroadcaster)
763 {
764 //JGH NOTE: looks like a listener against the MBeanServer is
765 //wrapped as a XMBean which has a broadcaster that is an NotificationEmitter
766 //but this resource target is a NotificationBroadcaster, in which case,
767 //w/o this .. you'll get a resource failure below
768 removeNotificationListener(listener);
769 }
770 else
771 {
772 throw new RuntimeMBeanException(new IllegalArgumentException("Target XXX is not a notification emitter"
773
774 // FIXME: add the XXX object name, store from registration
775 ));
776 }
777 }
778
779 public MBeanNotificationInfo[] getNotificationInfo()
780 {
781 return getNotificationInfoFromResource();
782 }
783
784 protected MBeanNotificationInfo[] getNotificationInfoFromResource()
785 {
786 if (resource instanceof NotificationBroadcaster)
787 {
788 return ((NotificationBroadcaster) resource).getNotificationInfo();
789 }
790 else
791 return new MBeanNotificationInfo[]{};
792 }
793
794
795 // MBeanInvoker implementation -----------------------------------
796
797 public MBeanInfo getMetaData()
798 {
799 return info;
800 }
801
802 public Object getResource()
803 {
804 return resource;
805 }
806
807 /**
808 * Sets the XMBean resource and optionally allows the resource to interact
809 * with the jmx microkernel via the following injection points:
810 * #ModelMBeanConstants.MBEAN_SERVER_INJECTION_TYPE
811 * #ModelMBeanConstants.MBEAN_INFO_INJECTION_TYPE
812 * #ModelMBeanConstants.OBJECT_NAME_INJECTION_TYPE
813 * @param resource - the model mbean resource
814 */
815 public void setResource(Object resource)
816 {
817 this.resource = resource;
818 }
819
820 public ObjectName getObjectName()
821 {
822 if (resourceEntry == null)
823 return null;
824 else
825 return resourceEntry.getObjectName();
826 }
827
828 public void updateAttributeInfo(Descriptor attrDesc) throws MBeanException
829 {
830 ModelMBeanInfoSupport minfo = (ModelMBeanInfoSupport) info;
831 minfo.setDescriptor(attrDesc, ModelMBeanConstants.ATTRIBUTE_DESCRIPTOR);
832 }
833
834 /**
835 * Add dynamically an operation interceptor, first in the chain.
836 */
837 public void addOperationInterceptor(Interceptor interceptor)
838 {
839 if (operationContextMap != null && interceptor != null)
840 {
841 // Go through all the operation InvocationContext and add the interceptor
842 for (Iterator it = operationContextMap.entrySet().iterator(); it.hasNext();)
843 {
844 Map.Entry entry = (Map.Entry) it.next();
845
846 InvocationContext ctx = (InvocationContext) entry.getValue();
847 List list = ctx.getInterceptors();
848
849 // to make the interceptor list update atomic, make a new ArrayList,
850 // add the new interceptor first and copy over the old ones,
851 // then update the context
852 List newList = new ArrayList();
853 newList.add(interceptor);
854
855 if (list != null)
856 {
857 newList.addAll(list);
858 }
859
860 ctx.setInterceptors(newList);
861 }
862 }
863 }
864
865 /**
866 * Remove the specified operation interceptor
867 */
868 public void removeOperationInterceptor(Interceptor interceptor)
869 {
870 if (operationContextMap != null && interceptor != null)
871 {
872 // Go through all the operation InvocationContext and remove the interceptor
873 for (Iterator it = operationContextMap.entrySet().iterator(); it.hasNext();)
874 {
875 Map.Entry entry = (Map.Entry) it.next();
876
877 InvocationContext ctx = (InvocationContext) entry.getValue();
878 List list = ctx.getInterceptors();
879
880 // to make the interceptor list update atomic, make a copy of the list
881 // remove the interceptor (if found), then update the context
882 if (list != null)
883 {
884 List newList = new ArrayList(list);
885
886 // this should probably work, whether or not equals() is implemented
887 // it'll remove the first occurence
888 newList.remove(interceptor);
889
890 ctx.setInterceptors(newList);
891 }
892 }
893 }
894 }
895
896 // Other Public Methods ------------------------------------------
897
898 public void suspend()
899 {
900 }
901
902 public void suspend(long wait) throws TimeoutException
903 {
904 }
905
906 public void suspend(boolean force)
907 {
908 }
909
910 public boolean isSuspended()
911 {
912 return false;
913 }
914
915 public void setInvocationTimeout(long time)
916 {
917 }
918
919 public long getInvocationTimeout()
920 {
921 return 0l;
922 }
923
924 public void resume()
925 {
926 }
927
928 public MBeanServer getServer()
929 {
930 return server;
931 }
932
933 // Protected -----------------------------------------------------
934
935 /**
936 * Inject context from the xmbean layer to the resource
937 * @param type - the type of injection
938 * @param name - the setter method name of the resource
939 * @param argType - the injection data type
940 * @param value - the injection data value to pass to the setter
941 */
942 protected void inject(String type, String name, Class argType, Object value)
943 {
944 try
945 {
946 Class resClass = resource.getClass();
947 Class[] sig = {argType};
948 Method setter = resClass.getMethod(name, sig);
949 Object[] args = {value};
950 setter.invoke(resource, args);
951 }
952 catch(NoSuchMethodException e)
953 {
954 log.debug("Setter not found: "+name+"("+argType+")", e);
955 }
956 catch(Exception e)
957 {
958 log.warn("Failed to inject type: "+type+" using setter: "+name, e);
959 }
960 }
961
962 protected ObjectName invokePreRegister(MBeanServer server, ObjectName name)
963 throws Exception
964 {
965 if (resource instanceof MBeanRegistration)
966 return ((MBeanRegistration) resource).preRegister(server, name);
967
968 return name;
969 }
970
971 protected void invokePostRegister(Boolean b)
972 {
973 if (resource instanceof MBeanRegistration)
974 ((MBeanRegistration) resource).postRegister(b);
975 }
976
977 protected void invokePreDeregister() throws Exception
978 {
979 if (resource instanceof MBeanRegistration)
980 ((MBeanRegistration) resource).preDeregister();
981 }
982
983 protected void invokePostDeregister()
984 {
985 if (resource instanceof MBeanRegistration)
986 ((MBeanRegistration) resource).postDeregister();
987 }
988
989 protected void initAttributeContexts(MBeanAttributeInfo[] attributes)
990 {
991 // create invocation contexts for attributes
992 for (int i = 0; i < attributes.length; ++i)
993 {
994 InvocationContext ctx = new InvocationContext();
995
996 // fill in some default values, the attribute name
997 ctx.setName(attributes[i].getName());
998
999 ctx.setAttributeType(attributes[i].getType());
1000
1001 // set myself as the invoker
1002 ctx.setInvoker(this);
1003
1004 //ctx.add(InvocationContext.ATTRIBUTE_ACCESS, getAccessCode(attributes[i]));
1005
1006 // store
1007 attributeContextMap.put(attributes[i].getName(), ctx);
1008 }
1009 if (log.isTraceEnabled())
1010 log.trace(getObjectName() + " configured attribute contexts: " + operationContextMap);
1011 }
1012
1013 protected void initOperationContexts(MBeanOperationInfo[] operations)
1014 {
1015 // create invocation contexts for operations
1016 for (int i = 0; i < operations.length; ++i)
1017 {
1018 InvocationContext ctx = new InvocationContext();
1019
1020 // extract operation name + signature
1021 String opName = operations[i].getName();
1022 MBeanParameterInfo[] signature = operations[i].getSignature();
1023 String returnType = operations[i].getReturnType();
1024
1025 // name is unchanged, fill in the context
1026 ctx.setName(opName);
1027
1028 // signature doesn't change..
1029 ctx.setSignature(signature);
1030
1031 // return type
1032 ctx.setReturnType(returnType);
1033
1034 // set myself as the invoker
1035 ctx.setInvoker(this);
1036
1037 // add impact as part of ctx map (rarely accessed information)
1038 //ctx.add(InvocationContext.OPERATION_IMPACT, operations[i].getImpact());
1039
1040 // create an operation key consisting of the name + signature
1041 // (required for overloaded operations)
1042 OperationKey opKey = new OperationKey(opName, signature);
1043
1044 // store
1045 operationContextMap.put(opKey, ctx);
1046 }
1047
1048 if (log.isTraceEnabled())
1049 log.trace(getObjectName() + " configured operation contexts: " + operationContextMap);
1050 }
1051
1052 protected void initDispatchers()
1053 {
1054 boolean trace = log.isTraceEnabled();
1055
1056 // locate the resource class to receive the invocations
1057 Class clazz = null;
1058 if (resource != null)
1059 {
1060 clazz = resource.getClass();
1061
1062 // JBAS-1704, if the target class is *not* public, look for
1063 // an exposed MBean interface, if one exists.
1064 // This should be checking if we are dealing with a standard
1065 // mbean (but not a standard mbean deployed as a model mbean)
1066 // but it doesn't look convenient from this baseclass.
1067 if (Modifier.isPublic(clazz.getModifiers()) == false)
1068 {
1069 clazz = StandardMetaData.findStandardInterface(clazz);
1070 }
1071 }
1072
1073 // map the Methods on the target resource for easy access
1074 MethodMapper mmap = new MethodMapper(clazz);
1075 if (trace)
1076 log.trace(getObjectName() + " " + clazz + " map=" + mmap);
1077
1078 MBeanOperationInfo[] operations = info.getOperations();
1079
1080 // Set the dispatchers for the operations
1081 for (int i = 0; i < operations.length; ++i)
1082 {
1083 MBeanOperationInfo op = operations[i];
1084 OperationKey opKey = new OperationKey(op.getName(), op.getSignature());
1085 InvocationContext ctx = (InvocationContext) operationContextMap.get(opKey);
1086
1087 Interceptor dispatcher = ctx.getDispatcher();
1088
1089 // Reconfigure if we have a Null or Reflected dispatcher
1090 if (dispatcher instanceof NullDispatcher || (dispatcher instanceof ReflectedDispatcher))
1091 {
1092 Object target = null;
1093 dispatcher = null;
1094 Method m = mmap.lookupOperation(op);
1095 if (m == null)
1096 {
1097 // Look for an method on the model mbean
1098 m = MethodMapper.lookupOperation(op, this);
1099 if (m != null)
1100 {
1101 // operation found on the 'this' invoker
1102 target = this;
1103 dispatcher = new ReflectedDispatcher(m, dynamicResource);
1104 }
1105 else
1106 {
1107 // operation not found, use late binding
1108 // What is this late binding attempt and should there be a warning?
1109 dispatcher = new ReflectedDispatcher(dynamicResource);
1110 }
1111 }
1112 else
1113 {
1114 // operation found on the resource
1115 target = resource;
1116 dispatcher = new ReflectedDispatcher(m, dynamicResource);
1117 }
1118 if (trace)
1119 log.trace(getObjectName() + " will dispatch op=" + opKey +
1120 " to " + Strings.defaultToString(target) +
1121 " method= " + m);
1122 ctx.setTarget(target);
1123 ctx.setDispatcher(dispatcher);
1124 }
1125 }
1126
1127 // Set the dispatchers for the attributes with getters/setters
1128 MBeanAttributeInfo[] attributes = info.getAttributes();
1129 for (int i = 0; i < attributes.length; ++i)
1130 {
1131 MBeanAttributeInfo attribute = attributes[i];
1132 String name = attribute.getName();
1133 InvocationContext ctx = (InvocationContext) attributeContextMap.get(name);
1134
1135 Method getter = mmap.lookupGetter(attribute);
1136 Method setter = mmap.lookupSetter(attribute);
1137 ctx.setDispatcher(new AttributeDispatcher(getter, setter, dynamicResource));
1138 ctx.setTarget(resource);
1139 }
1140 }
1141
1142 /**
1143 * Placeholder to allow subclasses to override the invocation
1144 * @param invocation the invocation
1145 * @throws MBeanException for any error
1146 */
1147 protected void override(Invocation invocation) throws MBeanException
1148 {
1149 }
1150
1151 protected String getSignatureString(String[] signature)
1152 {
1153 if (signature == null)
1154 return "()";
1155 if (signature.length == 0)
1156 return "()";
1157
1158 StringBuffer sbuf = new StringBuffer(512);
1159
1160 sbuf.append("(");
1161 for (int i = 0; i < signature.length - 1; ++i)
1162 {
1163 sbuf.append(signature[i]);
1164 sbuf.append(",");
1165 }
1166 sbuf.append(signature[signature.length - 1]);
1167 sbuf.append(")");
1168
1169 return sbuf.toString();
1170 }
1171
1172
1173 // Inner classes -------------------------------------------------
1174 protected final class OperationKey
1175 {
1176 String[] keys = null;
1177 int hash = 0;
1178
1179 public OperationKey(final String name, final String type)
1180 {
1181 if (type != null)
1182 {
1183 keys = new String[2];
1184
1185 keys[0] = name;
1186 keys[1] = type;
1187
1188 hash = name.hashCode();
1189 }
1190
1191 else
1192 {
1193 keys = new String[]{name};
1194 hash = name.hashCode();
1195 }
1196 }
1197
1198 public OperationKey(final String name, final String[] signature)
1199 {
1200 if (signature != null)
1201 {
1202 keys = new String[signature.length + 1];
1203
1204 keys[0] = name;
1205
1206 System.arraycopy(signature, 0, keys, 1, signature.length);
1207
1208 hash = name.hashCode();
1209 }
1210
1211 else
1212 {
1213 keys = new String[]{name};
1214 hash = name.hashCode();
1215 }
1216 }
1217
1218 public OperationKey(String name, MBeanParameterInfo[] signature)
1219 {
1220 if (signature == null)
1221 signature = new MBeanParameterInfo[0];
1222
1223 keys = new String[signature.length + 1];
1224
1225 keys[0] = name;
1226
1227 for (int i = 0; i < signature.length; ++i)
1228 {
1229 keys[i + 1] = signature[i].getType();
1230 }
1231
1232 hash = name.hashCode();
1233 }
1234
1235 public OperationKey(MBeanOperationInfo info)
1236 {
1237 this(info.getName(), info.getSignature());
1238 }
1239
1240 public int hashCode()
1241 {
1242 return hash;
1243 }
1244
1245 public boolean equals(Object o)
1246 {
1247 OperationKey target = (OperationKey) o;
1248
1249 if (target.keys.length != keys.length)
1250 return false;
1251
1252 for (int i = 0; i < keys.length; ++i)
1253 {
1254 if (!(keys[i].equals(target.keys[i])))
1255 return false;
1256 }
1257
1258 return true;
1259 }
1260
1261 public String toString()
1262 {
1263 StringBuffer buffer = new StringBuffer(50);
1264 buffer.append(keys[0]).append("(");
1265
1266 for (int i = 1; i < keys.length - 1; ++i)
1267 {
1268 buffer.append(keys[i]).append(',');
1269 }
1270
1271 if (keys.length > 1)
1272 buffer.append(keys[keys.length - 1]);
1273 buffer.append(")");
1274 return buffer.toString();
1275 }
1276 }
1277
1278 private void rethrowAsMBeanException(Throwable t) throws MBeanException
1279 {
1280 if (t instanceof RuntimeException)
1281 throw new RuntimeMBeanException((RuntimeException) t);
1282 else if (t instanceof Error)
1283 throw new RuntimeErrorException((Error) t);
1284 else
1285 throw new MBeanException((Exception) t);
1286 }
1287
1288 private void rethrowAsRuntimeMBeanException(Throwable t)
1289 {
1290 if (t instanceof RuntimeException)
1291 throw new RuntimeMBeanException((RuntimeException) t);
1292 else if (t instanceof Error)
1293 throw new RuntimeErrorException((Error) t);
1294 else
1295 throw new RuntimeMBeanException(new RuntimeException("Unhandled exception", t));
1296 }
1297 }