001/*
002 *  Copyright 2021 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.core.mbean;
017
018import javax.management.Attribute;
019import javax.management.AttributeList;
020import javax.management.AttributeNotFoundException;
021import javax.management.DynamicMBean;
022import javax.management.InvalidAttributeValueException;
023import javax.management.MBeanAttributeInfo;
024import javax.management.MBeanConstructorInfo;
025import javax.management.MBeanException;
026import javax.management.MBeanInfo;
027import javax.management.MBeanNotificationInfo;
028import javax.management.MBeanOperationInfo;
029import javax.management.ReflectionException;
030import javax.management.RuntimeOperationsException;
031
032import org.ametys.runtime.plugin.component.AbstractLogEnabled;
033
034/**
035 * Abstract class for Ametys Dynamic MBean.
036 */
037public abstract class AbstractAmetysDynamicMBean extends AbstractLogEnabled implements DynamicMBean
038{
039    /** The MBean metadata */
040    protected MBeanInfo _mBeanInfo;
041    
042    /**
043     * AbstractAmetysDynamicMBean basic constructor.
044     * Generate the MBean info.
045     */
046    public AbstractAmetysDynamicMBean()
047    {
048        buildDynamicMBeanInfo();
049    }
050
051    @Override
052    public Object getAttribute(String attributeName) throws AttributeNotFoundException
053    {
054     // Check attribute_name to avoid NullPointerException later on
055        if (attributeName == null)
056        {
057            throw new RuntimeOperationsException(
058                new IllegalArgumentException("Attribute name cannot be null"), 
059                "Cannot invoke a getter of " + this.getClass().getName() + " with null attribute name");
060        }
061
062        Object attribute = _getAttribute(attributeName);
063
064        // If attribute_name has not been recognized
065        if (attribute == null)
066        {
067            throw new AttributeNotFoundException("Cannot find " + attributeName + " attribute in " + this.getClass().getName());
068        }
069        return attribute;
070    }
071
072    /** 
073     * Get the value of the attribute.
074     * This method should assume that attributeName is not null.
075     * And return null if the name is not recognized
076     * @param attributeName the name of the attribute
077     * @return the value of the attribute
078     */
079    protected abstract Object _getAttribute(String attributeName);
080
081    @Override
082    public AttributeList getAttributes(String[] attributeNames)
083    {
084     // Check attributeNames to avoid NullPointerException later on
085        if (attributeNames == null)
086        {
087            throw new RuntimeOperationsException(
088                new IllegalArgumentException(
089                    "attributeNames[] cannot be null"),
090                "Cannot invoke a getter of " + this.getClass().getName());
091        }
092        AttributeList resultList = new AttributeList();
093        
094        // if attributeNames is empty, return an empty result list
095        if (attributeNames.length == 0)
096        {
097            return resultList;
098        }
099        
100        // build the result attribute list
101        for (int i = 0; i < attributeNames.length; i++)
102        {
103            try
104            {
105                Object value = getAttribute(attributeNames[i]);     
106                resultList.add(new Attribute(attributeNames[i], value));
107            }
108            catch (Exception e)
109            {
110                getLogger().error("Failed to get attribute {}", attributeNames[i], e);
111            }
112        }
113        return resultList;
114    }
115    
116    @Override
117    public Object invoke(String actionName, Object[] params, String[] signature) throws MBeanException, ReflectionException
118    {
119        // we don't implement any method to invoke so no need
120        throw new UnsupportedOperationException("No invoke method defined for this MBean.");
121    }
122
123    @Override
124    public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException
125    {
126        // we don't need to set any attribute
127        throw new UnsupportedOperationException("Method to set attribute is not defined for this MBean.");
128    }
129
130    @Override
131    public AttributeList setAttributes(AttributeList attributes)
132    {
133        // we don't need to set any attribute
134        throw new UnsupportedOperationException("Method to set attributes is not defined for this MBean.");
135    }
136    
137    @Override
138    public MBeanInfo getMBeanInfo()
139    {
140        return _mBeanInfo;
141    }
142
143    
144    private void buildDynamicMBeanInfo()
145    {
146        _mBeanInfo = new MBeanInfo(getMBeanName(),
147                                   getMBeanDescription(),
148                                   getMBeanAttributes(),
149                                   getMBeanConstructorInfo(),
150                                   getMBeanOperationInfo(),
151                                   getMBeanNotificationInfo());
152    }
153
154    /**
155     * Get the human readable name of the MBean.
156     * This name will be exposed.
157     * @return the name
158     */
159    protected abstract String getMBeanName();
160
161    /**
162     * Get the description of the MBean
163     * @return the description
164     */
165    protected abstract String getMBeanDescription();
166
167    /**
168     * Get the definition of the attributes exposed by the MBean.
169     * @return the attributes information
170     */
171    protected abstract MBeanAttributeInfo[] getMBeanAttributes();
172
173    /**
174     * Get the definition of the constructors exposed by the MBean.
175     * @return the constructors information
176     */
177    protected MBeanConstructorInfo[] getMBeanConstructorInfo()
178    {
179        // We usually don't set any constructor
180        return new MBeanConstructorInfo[0];
181    }
182
183    /**
184     * Get the definition of the operation methods exposed by the MBean
185     * @return the operations information
186     */
187    protected MBeanOperationInfo[] getMBeanOperationInfo()
188    {
189        // We usually don't set any operation
190        return new MBeanOperationInfo[0];
191    }
192
193    /**
194     * Get the definition of the notification exposed by the MBean
195     * @return the notification information
196     */
197    protected MBeanNotificationInfo[] getMBeanNotificationInfo()
198    {
199        // We usually don't set any notification
200        return new MBeanNotificationInfo[0];
201    }
202    
203    /**
204     * render a human readable filesize
205     * @param size the size in byte
206     * @return the human readable string
207     */
208    protected String _fileSize(long size)
209    {
210        final long  byteLimit = 1024;
211        final long kbLimit = 1048576;
212        final long mbLimit = 1073741824;
213        String fileSize;
214        if (size < byteLimit)
215        {
216            if (size == 1)
217            {
218                fileSize = "1 byte";
219            }
220            else
221            {
222                fileSize = String.format("%d bytes", size);
223            }
224        }
225        else if (size < kbLimit)
226        {
227            fileSize = String.format("%.2f KB", ((double) size) / byteLimit);
228        }
229        else if (size < mbLimit)
230        {
231            fileSize = String.format("%.2f MB", ((double) size) / kbLimit);
232        }
233        else
234        {
235            fileSize = String.format("%.2f GB", ((double) size) / mbLimit);
236        }
237        
238        return fileSize;
239    }
240}