View Javadoc
1   /*
2    * *************************************************************************************************************************************************************
3    *
4    * TheseFoolishThings: Miscellaneous utilities
5    * http://tidalwave.it/projects/thesefoolishthings
6    *
7    * Copyright (C) 2009 - 2025 by Tidalwave s.a.s. (http://tidalwave.it)
8    *
9    * *************************************************************************************************************************************************************
10   *
11   * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12   * You may obtain a copy of the License at
13   *
14   *     http://www.apache.org/licenses/LICENSE-2.0
15   *
16   * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17   * CONDITIONS OF ANY KIND, either express or implied.  See the License for the specific language governing permissions and limitations under the License.
18   *
19   * *************************************************************************************************************************************************************
20   *
21   * git clone https://bitbucket.org/tidalwave/thesefoolishthings-src
22   * git clone https://github.com/tidalwave-it/thesefoolishthings-src
23   *
24   * *************************************************************************************************************************************************************
25   */
26  package it.tidalwave.actor.impl;
27  
28  import java.lang.management.ManagementFactory;
29  import java.lang.reflect.Modifier;
30  import javax.annotation.Nonnegative;
31  import javax.annotation.Nonnull;
32  import java.util.HashMap;
33  import java.util.Map;
34  import it.tidalwave.actor.spi.ActorActivatorStats;
35  import javax.management.InstanceAlreadyExistsException;
36  import javax.management.InstanceNotFoundException;
37  import javax.management.MBeanRegistrationException;
38  import javax.management.MalformedObjectNameException;
39  import javax.management.NotCompliantMBeanException;
40  import javax.management.ObjectName;
41  import lombok.Getter;
42  import lombok.extern.slf4j.Slf4j;
43  import static it.tidalwave.messagebus.spi.ReflectionUtils.*;
44  
45  /***************************************************************************************************************************************************************
46   *
47   * @author  Fabrizio Giudici
48   *
49   **************************************************************************************************************************************************************/
50  @Slf4j
51  public class MBeansManager
52    {
53      @Nonnull
54      private final Object actorObject;
55  
56      @Nonnull @Getter
57      private final ActorActivatorStats stats;
58  
59      private ObjectName statsName;
60  
61      private final Map<ObjectName, Object> mbeansMapByName = new HashMap<>();
62  
63      /***********************************************************************************************************************************************************
64       *
65       **********************************************************************************************************************************************************/
66      public MBeansManager (@Nonnull final Object actorObject, @Nonnegative final int poolSize)
67        {
68          this.actorObject = actorObject;
69          stats = new ActorActivatorStats(poolSize);
70        }
71  
72      /***********************************************************************************************************************************************************
73       *
74       **********************************************************************************************************************************************************/
75      public void register()
76        {
77          forEachMethodInTopDownHierarchy(actorObject, new MethodProcessorSupport()
78            {
79              @Override @Nonnull
80              public FilterResult filter (@Nonnull final Class<?> clazz)
81                {
82                  mbeansMapByName.putAll(getMBeans(actorObject, clazz));
83                  return FilterResult.IGNORE;
84                }
85            });
86  
87          final var mBeanServer = ManagementFactory.getPlatformMBeanServer();
88  
89          try
90            {
91              final var name = String.format("%s:type=%s", actorObject.getClass().getPackage().getName(),
92                                             actorObject.getClass().getSimpleName());
93              statsName = new ObjectName(name);
94              mBeanServer.registerMBean(stats, statsName);
95            }
96          catch (InstanceAlreadyExistsException | MalformedObjectNameException | NotCompliantMBeanException | MBeanRegistrationException e)
97            {
98              log.error("Cannot register master MBean for actor " + actorObject, e);
99            }
100 
101         for (final var entry : mbeansMapByName.entrySet())
102           {
103             try
104               {
105                 log.info(">>>> registering MBean {}", entry);
106                 mBeanServer.registerMBean(entry.getValue(), entry.getKey());
107               }
108             catch (InstanceAlreadyExistsException | NotCompliantMBeanException | MBeanRegistrationException e)
109               {
110                 log.error("Cannot register MBean: " + entry, e);
111               }
112           }
113       }
114 
115     /***********************************************************************************************************************************************************
116      * Unregisters the MBeans.
117      **********************************************************************************************************************************************************/
118     public void unregister()
119       {
120         final var mBeanServer = ManagementFactory.getPlatformMBeanServer();
121 
122         try
123           {
124             mBeanServer.unregisterMBean(statsName);
125           }
126         catch (InstanceNotFoundException | MBeanRegistrationException e)
127           {
128             log.error("Cannot register master MBean for actor " + actorObject, e);
129           }
130 
131         for (final var entry : mbeansMapByName.entrySet())
132           {
133             try
134               {
135                 log.info(">>>> unregistering MBean {}", entry);
136                 mBeanServer.unregisterMBean(entry.getKey());
137               }
138             catch (InstanceNotFoundException | MBeanRegistrationException e)
139               {
140                 log.error("Cannot unregister MBean: " + entry, e);
141               }
142           }
143       }
144 
145     /***********************************************************************************************************************************************************
146      *
147      **********************************************************************************************************************************************************/
148     @Nonnull
149     private static Map<ObjectName, Object> getMBeans (@Nonnull final Object actorObject, @Nonnull final Class<?> clazz)
150       {
151         final Map<ObjectName, Object> result = new HashMap<>();
152 
153         for (final var field : clazz.getDeclaredFields())
154           {
155             if (!field.isSynthetic() && ((field.getModifiers() & Modifier.STATIC) == 0))
156               {
157                 try
158                   {
159                     field.setAccessible(true);
160                     final var value = field.get(actorObject);
161 
162                     if (value != null)
163                       {
164                         final var interfaces = value.getClass().getInterfaces();
165                         //
166                         // TODO: it checks only first interface - what about an annotation?
167                         //
168                         if ((interfaces.length > 0) && interfaces[0].getName().endsWith("MBean"))
169                           {
170                             final var name = String.format("%s:type=%s", clazz.getPackage().getName(),
171                                                            value.getClass().getSimpleName());
172                             result.put(new ObjectName(name), value);
173                           }
174                       }
175                   }
176                 catch (IllegalArgumentException | MalformedObjectNameException | IllegalAccessException e)
177                   {
178                     log.error("Cannot handle object: {}", field);
179                   }
180               }
181           }
182 
183         return result;
184       }
185   }