001/*
002 * ============================================================================
003 * Copyright © 2002-2023 by Thomas Thrien.
004 * All Rights Reserved.
005 * ============================================================================
006 * Licensed to the public under the agreements of the GNU Lesser General Public
007 * License, version 3.0 (the "License"). You may obtain a copy of the License at
008 *
009 *      http://www.gnu.org/licenses/lgpl.html
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014 * License for the specific language governing permissions and limitations
015 * under the License.
016 */
017
018package org.tquadrat.foundation.mgmt;
019
020import static java.lang.String.format;
021import static org.apiguardian.api.API.Status.STABLE;
022import static org.tquadrat.foundation.lang.CommonConstants.EMPTY_CHARSEQUENCE;
023import static org.tquadrat.foundation.lang.DebugOutput.ifDebug;
024import static org.tquadrat.foundation.lang.Objects.isNull;
025import static org.tquadrat.foundation.lang.Objects.nonNull;
026import static org.tquadrat.foundation.lang.Objects.requireNotEmptyArgument;
027
028import javax.management.InstanceNotFoundException;
029import javax.management.MBeanRegistrationException;
030import javax.management.MalformedObjectNameException;
031import javax.management.ObjectName;
032import java.util.ArrayList;
033import java.util.List;
034import java.util.StringJoiner;
035
036import org.apiguardian.api.API;
037import org.tquadrat.foundation.annotation.ClassVersion;
038import org.tquadrat.foundation.annotation.UtilityClass;
039import org.tquadrat.foundation.exception.PrivateConstructorForStaticClassCalledError;
040import org.tquadrat.foundation.lang.NameValuePair;
041
042/**
043 *  This class provides some utilities that are useful in the context of JMX.
044 *
045 *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
046 *  @version $Id: JMXUtils.java 1070 2023-09-29 17:09:34Z tquadrat $
047 *  @since 0.0.1
048 *
049 *  @UMLGraph.link
050 */
051@UtilityClass
052@ClassVersion( sourceVersion = "$Id: JMXUtils.java 1070 2023-09-29 17:09:34Z tquadrat $" )
053@API( status = STABLE, since = "0.0.1" )
054public final class JMXUtils
055{
056        /*-----------*\
057    ====** Constants **========================================================
058        \*-----------*/
059    /**
060     *  The property name for the connector address: {@value}.
061     */
062    public static final String CONNECTOR_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress";
063
064    /**
065     *  The name of the JMX domain that is used by all JMX enabled components
066     *  of the library.
067     */
068    public static final String JMX_DOMAIN = "org.tquadrat";
069
070    /**
071     *  The property name for the class of an MBean: {@value}.
072     */
073    public static final String MBEAN_CLASS = "class";
074
075    /**
076     *  The property name for the function of an MBean: {@value}.
077     */
078    public static final String MBEAN_FUNCTION = "function";
079
080    /**
081     *  The property name for the loader of an MBean: {@value}.
082     */
083    public static final String MBEAN_LOADER = "loader";
084
085    /**
086     *  The property name for the name of an MBean: {@value}.
087     */
088    public static final String MBEAN_NAME = "name";
089
090    /**
091     *  The property name for the MBean type: {@value}.
092     */
093    public static final String MBEAN_TYPE = "type";
094
095        /*--------------*\
096    ====** Constructors **=====================================================
097        \*--------------*/
098    /**
099     *  No instance allowed for this class.
100     */
101    private JMXUtils() { throw new PrivateConstructorForStaticClassCalledError( JMXUtils.class ); }
102
103        /*---------*\
104    ====** Methods **==========================================================
105        \*---------*/
106    /**
107     *  <p>{@summary Composes an object name from the given domain name and the
108     *  given properties.}</p>
109     *  <p>The object name has the form</p>
110     *  <pre><code>    &lt;Domain&gt;:type=&lt;Type&gt;,function=&lt;Function&gt;<b>[</b>, class=&lt;Class&gt;<b>]</b><b>[</b>,…<b>]</b></code></pre>
111     *  <p>The type is something like a category.</p>
112     *  <p>The function is a description for what the MBean does.</p>
113     *  <p>The class can be provided, if multiple MBean implementations with
114     *  the same type and function will be loaded.</p>
115     *  <p>Additional properties in the form
116     *  <code>&lt;name&gt;=&lt;value&gt;</code> can be added as required.</p>
117     *
118     *  @param  domainName  The domain name.
119     *  @param  type    The type of the MBean that will be named with the new
120     *      object name.
121     *  @param  function    The function of the MBean.
122     *  @param  mbeanClass  The MBean's class; can be {@code null}.
123     *  @param  properties  Additional properties as name-value-pairs; can be
124     *      {@code null}.
125     *  @return The object name.
126     *  @throws MalformedObjectNameException    It is not possible to create a
127     *      valid object name from the given domain name and properties.
128     */
129    @SafeVarargs
130    @API( status = STABLE, since = "0.0.1" )
131    public static ObjectName composeObjectName( final String domainName, final String type, final String function, final Class<?> mbeanClass, final NameValuePair<String>... properties ) throws MalformedObjectNameException
132    {
133        final var propertyList = new ArrayList<NameValuePair<String>> ();
134        if( nonNull( properties ) ) propertyList.addAll( List.of( properties ) );
135        if( nonNull( mbeanClass ) ) propertyList.add( new NameValuePair<>( MBEAN_CLASS, mbeanClass.getName() ) );
136        propertyList.add( new NameValuePair<>( MBEAN_FUNCTION, requireNotEmptyArgument( function, "function" ) ) );
137        propertyList.add( new NameValuePair<>( MBEAN_TYPE, requireNotEmptyArgument( type, "type" ) ) );
138
139        @SuppressWarnings( "unchecked" )
140        final var retValue = composeObjectName( domainName, propertyList.toArray( NameValuePair []::new ) );
141
142        //---* Done *----------------------------------------------------------
143        return retValue;
144    }   //  composeObjectName()
145
146    /**
147     *  Composes an object name from the given domain name and the given
148     *  properties.
149     *
150     *  @param  domainName  The domain name.
151     *  @param  properties  The properties as name-value-pairs; at least one
152     *      property has to be provided.
153     *  @return The object name.
154     *  @throws MalformedObjectNameException    It is not possible to create a
155     *      valid object name from the given domain name and properties.
156     */
157    @SafeVarargs
158    @API( status = STABLE, since = "0.0.1" )
159    public static ObjectName composeObjectName( final String domainName, final NameValuePair<String>... properties ) throws MalformedObjectNameException
160    {
161        final var name = new StringJoiner( ",", format( "%s:", requireNotEmptyArgument( domainName, "domainName" ) ), EMPTY_CHARSEQUENCE );
162        for( final var property : requireNotEmptyArgument( properties, "properties" ) )
163        {
164            name.add( toString( property ) );
165        }
166        final var retValue = new ObjectName( name.toString() );
167
168        //---* Done *----------------------------------------------------------
169        return retValue;
170    }   //  composeObjectName()
171
172    /**
173     *  Converts an instance of
174     *  {@link NameValuePair}
175     *  to a String.
176     *
177     *  @param  pair    The name-value-pair.
178     *  @return The String representation of the name-value-pair.
179     */
180    private static final String toString( final NameValuePair<String> pair )
181    {
182        if( isNull( pair.value() ) ) throw new IllegalArgumentException( "value is null" );
183        final var retValue = format( "%1$s=%2$s", pair.name(), pair.value() );
184
185        //---* Done *----------------------------------------------------------
186        return retValue;
187    }   // toString()
188
189    /**
190     *  Unregisters the given MBean from the MBeanServer. All exceptions – if
191     *  any – will be swallowed silently.
192     *
193     *  @param  mbean   The mbean to unregister; may be {@code null}.
194     */
195    @API( status = STABLE, since = "0.0.1" )
196    public static void unregisterQuietly( final JMXSupport<?> mbean )
197    {
198        if( nonNull( mbean ) )
199        {
200            try
201            {
202                mbean.unregister();
203            }
204            catch( final InstanceNotFoundException | MBeanRegistrationException e )
205            {
206                ifDebug( e );
207                /* Deliberately ignored */
208            }
209        }
210    }   //  unregisterQuietly()
211}
212//  class ManagementUtils
213
214/*
215 *  End of File
216 */