/* * Copyright (c) 2010 Ecole des Mines de Nantes. * * This file is part of Entropy. * * Entropy is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Entropy is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Entropy. If not, see <http://www.gnu.org/licenses/>. */ package entropy; import java.io.IOException; import java.lang.management.ManagementFactory; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import javax.management.InstanceAlreadyExistsException; import javax.management.InstanceNotFoundException; import javax.management.MBeanRegistrationException; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import entropy.controlLoop.ControlLoop; /** * This class aims at launching entropy. * * @author Fabien Hermenier */ public final class Entropy extends Thread implements EntropyMBean { /** * The logger associated to entropy. */ private static final Logger LOGGER = LoggerFactory.getLogger("Entropy"); /** * The name of the mbean. */ public static final String MBEAN_NAME = "entropy:type=controlLoop"; /** * Number of milliseconds in one second. */ public static final long SECONDS = 1000L; /** * The sleep delay in seconds between two iterations. */ private int sleepDelay; /** * The control loop tp */ private ControlLoop controlLoop; /** * The connector to link the MBean to the RMI registry. */ private JMXConnectorServer cs; /** * The registry port. */ private int port; /** * Indicates if the control loop has to terminate or not. */ private Boolean hasToShutdown = false; /** * The lock used to wait while requesting a shutdown. */ private final Object stopLock; /** * The lock used to get the state of entropy. */ private final Object stateLock; /** * Indicates wether Entropy is running or not */ private boolean isRunning; /** * The thread that run the control loop. */ private Thread t; /** * Launch Entropy with a specific control loop. * * @param loop the control loop to use */ public Entropy(ControlLoop loop) { this.controlLoop = loop; this.stateLock = new Object(); this.isRunning = false; stopLock = new Object(); } /** * Get the logger. * * @return an initialized loger */ public static Logger getLogger() { return LOGGER; } /** * Set the binding port of the registry. * * @param p the port */ public void setRegistryPort(int p) { this.port = p; } /** * Get the binding port of the registry. * * @return the port */ public int getRegistryPort() { return this.port; } /** * Set the sleep delay between two iterations. * * @param sec the delay in seconds. */ public void setSleepDelay(int sec) { this.sleepDelay = sec; } /** * Get the sleep delay between two iterations. * * @return a delay in seconds */ public int getSleepDelay() { return this.sleepDelay; } /** * Indicates wether Entropy must exit or not. * * @return true if the control loop demand to exit */ protected boolean mustExit() { boolean ret; synchronized (this.stopLock) { ret = this.hasToShutdown; } return ret; } @Override public void shutdown() throws EntropyException { if (!isRunning()) { throw new EntropyException("ControlLoop is not running"); } getLogger().debug("Waiting for the stoplock"); synchronized (this.stopLock) { this.hasToShutdown = Boolean.TRUE; } getLogger().debug("Releasing the stoplock"); try { this.cs.stop(); MBeanServer srv = ManagementFactory.getPlatformMBeanServer(); srv.unregisterMBean(new ObjectName(MBEAN_NAME)); getLogger().info("Control loop is no more binded"); t.join(); synchronized (this.stateLock) { this.isRunning = false; } } catch (IOException e) { throw new EntropyException(e.getMessage(), e); } catch (InterruptedException e) { throw new EntropyException(e.getMessage(), e); } catch (MalformedObjectNameException e) { throw new EntropyException(e.getMessage(), e); } catch (InstanceNotFoundException e) { throw new EntropyException(e.getMessage(), e); } catch (MBeanRegistrationException e) { throw new EntropyException(e.getMessage(), e); } } /** * Check wether Entropy is running or not * * @return true if Entropy is running */ public boolean isRunning() { boolean b; synchronized (this.stateLock) { b = isRunning; } return b; } /** * Start a control loop in another thread and register Entropy to allow a remote shutdown * * @throws EntropyException if an error occurred while starting the loop. */ public void startup() throws EntropyException { Registry registry; try { registry = LocateRegistry.getRegistry(port); //This registry is not necessarily running //to really check the access to the registry //we perform a list that fail if the access is not OK //(if there is no existing registry) registry.list(); getLogger().debug("Using existing RMI registry on localhost:" + port); } catch (RemoteException e) { getLogger().debug("Creating a RMI registry on localhost:" + port); try { LocateRegistry.createRegistry(port); } catch (RemoteException e1) { throw new EntropyException(e1.getMessage(), e1); } } MBeanServer srv = ManagementFactory.getPlatformMBeanServer(); try { srv.registerMBean(this, new ObjectName(MBEAN_NAME)); String url = "service:jmx:rmi:///jndi/rmi://localhost:" + port + "/" + MBEAN_NAME; JMXServiceURL serviceURL = new JMXServiceURL(url); this.cs = JMXConnectorServerFactory.newJMXConnectorServer(serviceURL, null, srv); this.cs.start(); getLogger().info("Control loop binded on the following URL: " + url); } catch (IOException e) { throw new EntropyException(e.getMessage(), e); } catch (NotCompliantMBeanException e) { throw new EntropyException(e.getMessage(), e); } catch (InstanceAlreadyExistsException e) { throw new EntropyException(e.getMessage(), e); } catch (MalformedObjectNameException e) { throw new EntropyException(e.getMessage(), e); } catch (MBeanRegistrationException e) { throw new EntropyException(e.getMessage(), e); } synchronized (this.stateLock) { this.isRunning = true; } this.start(); } /** * Inifinite loop in the separated thread. */ @Override public void run() { t = Thread.currentThread(); getLogger().info("Infinite loop started " + Thread.currentThread()); while (!this.mustExit()) { getLogger().debug("Starting a new loop iteration"); synchronized (this.stopLock) { this.controlLoop.runLoop(); } if (mustExit()) { this.controlLoop.destroy(); break; } getLogger().debug("Waiting ..."); try { Thread.sleep(this.sleepDelay * SECONDS); } catch (InterruptedException e) { getLogger().error(e.getMessage(), e); } } getLogger().info("Infinite loop ended"); } }