Java tutorial
/** * Copyright (C) : 2012 * * Synchrotron Soleil * L'Orme des merisiers * Saint Aubin * BP48 * 91192 GIF-SUR-YVETTE CEDEX * * This file is part of Tango. * * Tango 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. * * Tango 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 Tango. If not, see <http://www.gnu.org/licenses/>. */ package org.tango.server; import java.io.File; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.lang3.ArrayUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; import org.slf4j.ext.XLogger; import org.slf4j.ext.XLoggerFactory; import org.tango.client.database.DatabaseFactory; import org.tango.logging.LoggingManager; import org.tango.orb.ORBManager; import org.tango.server.annotation.Device; import org.tango.server.annotation.TransactionType; import org.tango.server.cache.TangoCacheManager; import org.tango.server.events.EventManager; import org.tango.server.export.TangoExporter; import org.tango.server.monitoring.MonitoringService; import org.tango.utils.DevFailedUtils; import fr.esrf.Tango.DevFailed; /** * Manage a tango server. * * @author ABEILLE * */ public final class ServerManager { private static final String SERVER_NAME_LOGGING = "serverName"; private static final String NODB = "-nodb"; /** * maximun length for device server name (255 characters) */ public static final int SERVER_NAME_MAX_LENGTH = 255; private static final String INIT_ERROR = "INIT_ERROR"; private final Logger logger = LoggerFactory.getLogger(ServerManager.class); private final XLogger xlogger = XLoggerFactory.getXLogger(ServerManager.class); private boolean useDb; private final AtomicBoolean isStarted = new AtomicBoolean(); private static final ServerManager INSTANCE = new ServerManager(); /** * The name of the executable */ private String execName; /** * The name of the instance */ private String instanceName; /** * execName/instanceName */ private String serverName; private String hostName; private String pid = "0"; private final Map<String, Class<?>> tangoClasses = new LinkedHashMap<String, Class<?>>(); private TangoExporter tangoExporter; private String lastClass; private TransactionType transactionType; private MonitoringService monitoring; private ServerManager() { Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { /** * shutdown hook */ @Override public void run() { MDC.put(SERVER_NAME_LOGGING, serverName); logger.debug("Shutdown hook unregister " + serverName); try { ServerManager.getInstance().stop(); } catch (final DevFailed e) { } } })); } /** * Get a ServerManager * * @return the server manager */ public static ServerManager getInstance() { return INSTANCE; } /** * Add a class to the server. * * @param tangoClass * The class name as defined in the tango database * @param deviceClass * The class that define a device with {@link Device} */ public void addClass(final String tangoClass, final Class<?> deviceClass) { lastClass = tangoClass; tangoClasses.put(tangoClass, deviceClass); } public void startDevice(final String deviceName, final Class<?> deviceClass) throws DevFailed { if (!isStarted.get()) { DevFailedUtils.throwDevFailed("the server must be started"); } if (tangoExporter != null) { if (tangoClasses.containsValue(deviceClass)) { tangoExporter.buildDevice(deviceName, deviceClass); } } } public void stopDevice(final String deviceName) throws DevFailed { if (!isStarted.get()) { DevFailedUtils.throwDevFailed("the server must be started"); } if (tangoExporter != null) { tangoExporter.unexportDevice(deviceName); } } /** * Starts a Tango server. The system property TANGO_HOST is mandatory if * using the tango database. If the tango db is not used, the system * property OAPort(for jacorb) must be set. The errors occurred will be only * logged. * * <pre> * ServerManager.getInstance().addClass(JTangoTest.class.getCanonicalName(), JTangoTest.class); * ServerManager.getInstance().start(new String[] { "1" }, "JTangoTest"); * </pre> * * @param args * The arguments to pass. instanceName [-v[trace level]] [-nodb * [-dlist <device name list>]] * @param execName * The name of the server as defined by Tango. * @see ServerManager#addClass(String, Class) */ public synchronized void start(final String[] args, final String execName) { if (!isStarted.get()) { try { init(args, execName); } catch (final DevFailed e) { DevFailedUtils.printDevFailed(e); } } } /** * Idem as start but throw exceptions. @see ServerManager#start(String[], * String) * * @param args * @param execName * @throws DevFailed */ public synchronized void startError(final String[] args, final String execName) throws DevFailed { if (isStarted.get()) { DevFailedUtils.throwDevFailed("this server is already started"); } init(args, execName); } /** * Starts a Tango server. The system property TANGO_HOST is mandatory if * using the tango database. If the tango db is not used the system property * OAPort(for jacorb) must be set. The errors occurred will be only logged. * * <pre> * ServerManager.getInstance().start(new String[] { "1" }, JTangoTest.class); * </pre> * * @param args * The arguments to pass. instanceName [-v[trace level]] [-nodb * [-dlist <device name list>]] * @param deviceClass * The class of the device. The server name and class name must * be defined in tango db with deviceClass.getSimpleName to be * started with this method. * @see ServerManager#addClass(String, Class) */ public synchronized void start(final String[] args, final Class<?> deviceClass) { if (!isStarted.get()) { addClass(deviceClass.getSimpleName(), deviceClass); try { init(args, deviceClass.getSimpleName()); } catch (final DevFailed e) { DevFailedUtils.printDevFailed(e); } } } private void init(final String[] args, final String execName) throws DevFailed { xlogger.entry(); // assume SLF4J is bound to logback in the current environment // final LoggerContext lc = (LoggerContext) // LoggerFactory.getILoggerFactory(); // // print logback's internal status // StatusPrinter.print(lc); // a server does not need graphics System.setProperty("java.awt.headless", "true"); this.execName = execName; // Manage command line option checkArgs(args); serverName = this.execName + "/" + instanceName; MDC.put(SERVER_NAME_LOGGING, serverName); logger.debug("Starting server {}", serverName); final StringBuilder tmp = new StringBuilder(serverName); // Check that the server name is not too long if (tmp.length() > SERVER_NAME_MAX_LENGTH) { DevFailedUtils.throwDevFailed(INIT_ERROR, "The device server name is too long! Max length is " + SERVER_NAME_MAX_LENGTH + " characters."); } isStarted.set(true); initPIDAndHostName(); final String toBeImported = Constants.ADMIN_DEVICE_DOMAIN + "/" + serverName; ORBManager.init(useDb, toBeImported); tangoExporter = new TangoExporter(hostName, serverName, pid, tangoClasses); tangoExporter.exportAll(); // start tango monitoring monitoring = new MonitoringService(serverName); monitoring.start(); logger.info("TANGO server {} started", serverName); // start the ORB ORBManager.startDetached(); xlogger.exit(); } /** * Stop the server and clear all * * @throws DevFailed */ public void stop() throws DevFailed { try { if (isStarted.get()) { tangoClasses.clear(); if (tangoExporter != null) { tangoExporter.clearClass(); tangoExporter.unexportAll(); } TangoCacheManager.shutdown(); EventManager.getInstance().close(); if (monitoring != null) { monitoring.stop(); } } } finally { ORBManager.shutdown(); logger.info("everything has been shutdown normally"); isStarted.set(false); } } /** * Get main argurments * * @return The usage */ private String getUsage() { return "usage : java -DTANGO_HOST=$TANGO_HOST " + execName + " instance_name [-v[trace level]] [-nodb [-dlist <device name list>] [-file=fileName]]"; } /** * Check the command line arguments. The first one is mandatory and is the * server name. A -v option is authorized with an optional argument. * * @param argv * @throws DevFailed */ private void checkArgs(final String[] argv) throws DevFailed { if (argv.length < 1) { DevFailedUtils.throwDevFailed(INIT_ERROR, getUsage()); } instanceName = argv[0]; useDb = true; DatabaseFactory.setUseDb(true); List<String> noDbDevices = new ArrayList<String>(); for (int i = 1; i < argv.length; i++) { final String arg = argv[i]; if (arg.startsWith("-h")) { // trace instance name System.out.println("instance list for server " + execName + ": " + Arrays.toString(DatabaseFactory.getDatabase().getInstanceNameList(execName))); } else if (arg.startsWith("-v")) { // logging level try { final int level = Integer.parseInt(arg.substring(arg.lastIndexOf('v') + 1)); LoggingManager.getInstance().setLoggingLevel(level, tangoClasses.values().toArray(new Class<?>[tangoClasses.size()])); } catch (final NumberFormatException e) { DevFailedUtils.throwDevFailed("Logging level error. Must be a number"); } } else if (arg.startsWith("-dlist")) { noDbDevices = configureNoDB(argv, i); useDb = false; } else if (arg.startsWith("-file")) { configureNoDBFile(argv, arg, noDbDevices); useDb = false; } } } /** * Configure {@link DatabaseFactory} without a tango db * * @param argv * @param currentIdx * @return The list of no db devices * @throws DevFailed */ private List<String> configureNoDB(final String argv[], final int currentIdx) throws DevFailed { final List<String> noDbDevices = new ArrayList<String>(); if (!ArrayUtils.contains(argv, NODB)) { DevFailedUtils.throwDevFailed(INIT_ERROR, getUsage()); } else { for (int j = currentIdx + 1; j < argv.length; j++) { if (!argv[j].startsWith("-")) { // several devices separated by commons final String[] devices = argv[j].split(","); if (devices.length > 1) { for (final String device : devices) { noDbDevices.add(device); } } else { noDbDevices.add(argv[j]); } logger.warn("Device with no db: " + argv[j]); } else { break; } } // only one class can be loaded with no db (the last one) DatabaseFactory.setNoDbDevices(noDbDevices.toArray(new String[noDbDevices.size()]), lastClass); } return noDbDevices; } /** * Configure {@link DatabaseFactory} without a tango db and a file for * properties * * @param argv * @param arg * @param noDbDevices * @throws DevFailed */ private void configureNoDBFile(final String argv[], final String arg, final List<String> noDbDevices) throws DevFailed { if (!ArrayUtils.contains(argv, NODB)) { DevFailedUtils.throwDevFailed(INIT_ERROR, getUsage()); } else { final String name = arg.split("=")[1]; final File file = new File(name); if (!file.exists() && !file.isFile()) { DevFailedUtils.throwDevFailed(INIT_ERROR, name + " does not exists or is not a file"); } logger.warn("Tango Database is not used - with file {} ", file.getPath()); DatabaseFactory.setDbFile(file, noDbDevices.toArray(new String[noDbDevices.size()]), lastClass); } } /** * Set the transaction type for all server. Overrides {@link Device#transactionType()} * * @param transactionType */ public void setTransactionType(final TransactionType transactionType) { this.transactionType = transactionType; } public TransactionType getTransactionType() { return transactionType; } /** * WARNING: it is jvm dependent (works for sun') * * @throws DevFailed */ private void initPIDAndHostName() throws DevFailed { final RuntimeMXBean rmxb = ManagementFactory.getRuntimeMXBean(); final String pidAndHost = rmxb.getName(); final String[] splitted = pidAndHost.split("@"); if (splitted.length > 1) { pid = splitted[0]; } try { final InetAddress addr; if (ORBManager.OAI_ADDR != null && !ORBManager.OAI_ADDR.isEmpty()) { addr = InetAddress.getByName(ORBManager.OAI_ADDR); } else { addr = InetAddress.getLocalHost(); } hostName = addr.getCanonicalHostName(); } catch (final UnknownHostException e) { DevFailedUtils.throwDevFailed(e); } logger.debug("pid: " + pid); logger.debug("hostName: " + hostName); } /** * The host on which this server is running * * @return the host name */ public String getHostName() { return hostName; } /** * The pid of this server * * @return the pid */ public String getPid() { return pid; } /** * execName/instanceName * * @return execName/instanceName */ public String getExecName() { return execName; } /** * The instance name * * @return The instance name */ public String getInstanceName() { return instanceName; } /** * The server name * * @return The server name */ public String getServerName() { return serverName; } /** * Get the started devices of this server. WARNING: result is filled after * server has been started * * @param tangoClass * @return The devices * @throws DevFailed */ public String[] getDevicesOfClass(final String tangoClass) throws DevFailed { return tangoExporter.getDevicesOfClass(tangoClass); } public String getAdminDeviceName() { return Constants.ADMIN_DEVICE_DOMAIN + "/" + serverName; } /** * * @return true is the server is running */ public boolean isStarted() { return isStarted.get(); } }