001/*
002 * #%L
003 * Netarchivesuite - common
004 * %%
005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library,
006 *             the National Library of France and the Austrian National Library.
007 * %%
008 * This program is free software: you can redistribute it and/or modify
009 * it under the terms of the GNU Lesser General Public License as
010 * published by the Free Software Foundation, either version 2.1 of the
011 * License, or (at your option) any later version.
012 * 
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Lesser Public License for more details.
017 * 
018 * You should have received a copy of the GNU General Lesser Public
019 * License along with this program.  If not, see
020 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
021 * #L%
022 */
023
024package dk.netarkivet.common.management;
025
026import java.util.Hashtable;
027
028import javax.management.InstanceAlreadyExistsException;
029import javax.management.JMException;
030import javax.management.MBeanServer;
031import javax.management.ObjectName;
032import javax.management.StandardMBean;
033
034import dk.netarkivet.common.CommonSettings;
035import dk.netarkivet.common.distribute.arcrepository.Replica;
036import dk.netarkivet.common.exceptions.ArgumentNotValid;
037import dk.netarkivet.common.exceptions.IOFailure;
038import dk.netarkivet.common.exceptions.IllegalState;
039import dk.netarkivet.common.exceptions.UnknownID;
040import dk.netarkivet.common.utils.Settings;
041import dk.netarkivet.common.utils.SystemUtils;
042
043/**
044 * Wrapper class for registering objects of type I as MBeans.
045 * <p>
046 * The register method will register a given object under an object name, generated with the domain given in
047 * constructor, and fields from the Hashtable nameProperties. It is prefilled with values common for all MBeans, but it
048 * is expected to be extended after the object is created with additional info.
049 *
050 * @param <I> The Type of Object to expose as an MBean. Currently only used with {@link SingleMBeanObject}
051 */
052public class SingleMBeanObject<I> {
053    /** Properties for the ObjectName name. */
054    private Hashtable<String, String> nameProperties = new Hashtable<String, String>();
055    /** The domain for this SingleMBeanObject. */
056    private String domain;
057    /** The object to expose as an MBean. */
058    private I exposedObject;
059    /** The interface, this SingleMBeanObject should expose on the given MBeanServer. */
060    private Class<I> asInterface;
061    /** The ObjectName this SingleMBeanObject registers on the given MBeanServer. */
062    private ObjectName name;
063    /** MBeanServer to register mbeans in. */
064    private final MBeanServer mBeanServer;
065
066    // FIXME Following environment constant are defined here in order to avoid
067    // refering to independent modules - the environment values are only
068    // used if defined
069    // Note that HARVESTER_HARVEST_CONTROLLER_CHANNEL should be identical to
070    // HarvesterSettings.HARVESTER_HARVEST_CONTROLLER_PRIORITY
071    private static String HARVESTER_HARVEST_CONTROLLER_CHANNEL = "settings.harvester.harvesting.channel";
072
073    /**
074     * Create a single mbean object. This will fill out nameProperties with default values, and remember the domain and
075     * interface given. It will not, however, register the MBean.
076     *
077     * @param domain The domain of the MBean.
078     * @param object The object to expose as an MBean.
079     * @param asInterface The interface this MBean is exposed as.
080     * @param mBeanServer The server to register the mbean in.
081     * @throws ArgumentNotValid If domain is null or empty, or any other argument is null.
082     */
083    public SingleMBeanObject(String domain, I object, Class<I> asInterface, MBeanServer mBeanServer) {
084        ArgumentNotValid.checkNotNullOrEmpty(domain, "String domain");
085        ArgumentNotValid.checkNotNull(object, "I object");
086        ArgumentNotValid.checkNotNull(asInterface, "Class asInterface");
087        ArgumentNotValid.checkNotNull(mBeanServer, "MBeanServer mbeanServer");
088        this.domain = domain;
089        this.asInterface = asInterface;
090        this.exposedObject = object;
091
092        nameProperties.put(Constants.PRIORITY_KEY_LOCATION, Settings.get(CommonSettings.THIS_PHYSICAL_LOCATION));
093        nameProperties.put(Constants.PRIORITY_KEY_MACHINE, SystemUtils.getLocalHostName());
094        nameProperties.put(Constants.PRIORITY_KEY_APPLICATIONNAME, Settings.get(CommonSettings.APPLICATION_NAME));
095        nameProperties.put(Constants.PRIORITY_KEY_APPLICATIONINSTANCEID,
096                Settings.get(CommonSettings.APPLICATION_INSTANCE_ID));
097        nameProperties.put(Constants.PRIORITY_KEY_HTTP_PORT, Settings.get(CommonSettings.HTTP_PORT_NUMBER));
098        try {
099            String val;
100            val = Settings.get(HARVESTER_HARVEST_CONTROLLER_CHANNEL);
101            nameProperties.put(Constants.PRIORITY_KEY_CHANNEL, val);
102        } catch (UnknownID e) {
103            nameProperties.put(Constants.PRIORITY_KEY_CHANNEL, "");
104        }
105        try {
106            String val = Replica.getReplicaFromId(Settings.get(CommonSettings.USE_REPLICA_ID)).getName();
107            nameProperties.put(Constants.PRIORITY_KEY_REPLICANAME, val);
108        } catch (UnknownID e) {
109            nameProperties.put(Constants.PRIORITY_KEY_REPLICANAME, "");
110        }
111
112        this.mBeanServer = mBeanServer;
113    }
114
115    /**
116     * Create a single mbean object.
117     * <p>
118     * This is a helper method for the constructor taking a domain, which take the domain from a preconstructed
119     * ObjectName and replaces the nameProperties with the properties from the given object name. Use this if you have
120     * an object name created already, which you wish to use.
121     *
122     * @param name The object name to register under.
123     * @param o The object to register.
124     * @param asInterface The interface o should implement.
125     * @param mBeanServer The mbean server to register o in.
126     * @throws ArgumentNotValid on any null parameter.
127     */
128    public SingleMBeanObject(ObjectName name, I o, Class<I> asInterface, MBeanServer mBeanServer) {
129        this(name.getDomain(), o, asInterface, mBeanServer);
130        nameProperties.clear();
131        nameProperties.putAll(name.getKeyPropertyList());
132    }
133
134    /**
135     * Properties for the ObjectName name. Update these before registering. On construction, initialised with location,
136     * hostname, httpport, priority, replica, applicationname, applicationinstid.
137     *
138     * @return Hashtable of the object name properties.
139     */
140    public Hashtable<String, String> getNameProperties() {
141        return nameProperties;
142    }
143
144    /**
145     * Registers this object as a standard MBean, with a name generated from domain given in constructor and the
146     * nameProperties hashTable.
147     *
148     * @throws IllegalState if bean is already registered.
149     * @throws IOFailure on trouble registering.
150     */
151    public void register() {
152        try {
153            name = new ObjectName(domain, nameProperties);
154            mBeanServer.registerMBean(new StandardMBean(exposedObject, asInterface), name);
155        } catch (InstanceAlreadyExistsException e) {
156            throw new IllegalState("this MBean '" + name + "' is already registered on the MBeanServer", e);
157        } catch (JMException e) {
158            throw new IOFailure("Unable to register MBean '" + name + "'", e);
159        }
160    }
161
162    /**
163     * Unregister the object from the MBeanServer. Note: It is not checked that it is actually this mbean that is
164     * registered under the name, this method simply unregisters any object with this object name.
165     *
166     * @throws IOFailure on trouble unregistering.
167     */
168    public void unregister() throws IOFailure {
169        MBeanServer mbserver = mBeanServer;
170        try {
171            if (name != null) {
172                name = new ObjectName(domain, nameProperties);
173            }
174            if (mbserver.isRegistered(name)) {
175                mbserver.unregisterMBean(name);
176            }
177        } catch (JMException e) {
178            throw new IOFailure("Unable to unregister MBean '" + name + "'", e);
179        }
180    }
181
182    /** @return the name */
183    public ObjectName getName() {
184        return name;
185    }
186
187}