package org.dcache.boot; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.util.StatusPrinter; import com.google.common.base.Strings; import com.google.common.primitives.Ints; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.FileSystemResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import dmg.cells.nucleus.CDC; import dmg.cells.nucleus.CellShell; import dmg.cells.nucleus.SystemCell; import dmg.util.CommandException; import org.dcache.util.Args; import org.dcache.util.ConfigurationProperties; import static org.dcache.boot.Properties.*; /** * Domain encapsulates the configuration of a domain and its * services. Provides the logic for starting a domain. */ public class Domain { private static final String SYSTEM_CELL_NAME = "System"; private static final Logger _log = LoggerFactory.getLogger(SystemCell.class); private final ConfigurationProperties _properties; private final List<ConfigurationProperties> _services; private final ResourceLoader _resourceLoader = new FileSystemResourceLoader(); public Domain(String name, ConfigurationProperties defaults) { _properties = new ConfigurationProperties(defaults, new DcacheConfigurationUsageChecker()); _properties.put(PROPERTY_DOMAIN_NAME, name); _services = new ArrayList<>(); } public ConfigurationProperties properties() { return _properties; } public List<String> getCellNames() { List<String> cells = new ArrayList<>(); for (ConfigurationProperties service: _services) { String cellName = Properties.getCellName(service); if (!Strings.isNullOrEmpty(cellName)) { cells.add(cellName); } } return cells; } public ConfigurationProperties createService(String source, LineNumberReader reader, String type) throws IOException { ConfigurationProperties service = new ConfigurationProperties(_properties, new DcacheConfigurationUsageChecker()); service.setIsService(true); service.put(PROPERTY_DOMAIN_SERVICE, type); service.load(source, reader); try { Resource batchFile = findBatchFile(service); service.put(PROPERTY_DOMAIN_SERVICE_BATCH, batchFile.getURI().toString()); checkExists(batchFile); Resource logConfiguration = getLogConfiguration(); if (logConfiguration != null) { checkExists(logConfiguration); } } catch (IOException e) { service.getProblemConsumer().setFilename(source); service.getProblemConsumer().setLineNumberReader(reader); service.getProblemConsumer().error(e.getMessage()); } _services.add(service); return service; } private void checkExists(Resource resource) throws IOException { if (!resource.exists()) { throw new IOException(resource.getDescription() + " (no such resource)"); } } public String getName() { return _properties.getValue(PROPERTY_DOMAIN_NAME); } List<ConfigurationProperties> getServices() { return _services; } public void start() throws Exception { initializeLogging(); String domainName = getName(); CDC.reset(SYSTEM_CELL_NAME, domainName); SystemCell systemCell = SystemCell.create(domainName, createCuratorFramework()); systemCell.start().get(); _log.info("Starting {}", domainName); executePreload(systemCell); for (ConfigurationProperties serviceConfig: _services) { executeService(systemCell, serviceConfig); } if (_services.isEmpty()) { _log.warn("No services found. Domain appears to be empty."); } } protected CuratorFramework createCuratorFramework() { int maxRetries = Integer.parseInt(_properties.getValue(PROPERTY_ZOOKEPER_RETRIES)); String zookeeperConnectionString = _properties.getValue(PROPERTY_ZOOKEPER_CONNECTION); int baseSleepTimeMs = getTime(PROPERTY_ZOOKEPER_SLEEP, PROPERTY_ZOOKEPER_SLEEP_UNIT); int connectionTimeoutMs = getTime(PROPERTY_ZOOKEPER_CONNECTION_TIMEOUT, PROPERTY_ZOOKEPER_CONNECTION_TIMEOUT_UNIT); int sessionTimeoutMs = getTime(PROPERTY_ZOOKEPER_SESSION_TIMEOUT, PROPERTY_ZOOKEPER_SESSION_TIMEOUT_UNIT); RetryPolicy retryPolicy = new ExponentialBackoffRetry(baseSleepTimeMs, maxRetries); return CuratorFrameworkFactory.newClient(zookeeperConnectionString, sessionTimeoutMs, connectionTimeoutMs, retryPolicy); } private int getTime(String baseProperty, String unitProperty) { TimeUnit timeUnit = TimeUnit.valueOf(_properties.getValue(unitProperty)); int duration = Integer.parseInt(_properties.getValue(baseProperty)); return Ints.checkedCast(timeUnit.toMillis(duration)); } private Resource getLogConfiguration() { String property = _properties.getValue(PROPERTY_LOG_CONFIG); return (property == null) ? null : _resourceLoader.getResource(property); } private void initializeLogging() throws URISyntaxException, IOException { try { Resource configuration = getLogConfiguration(); if (configuration == null) { return; } LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); loggerContext.reset(); for (String key: _properties.stringPropertyNames()) { loggerContext.putProperty(key, _properties.getProperty(key)); } try { JoranConfigurator configurator = new JoranConfigurator(); configurator.setContext(loggerContext); configurator.doConfigure(configuration.getURL()); } finally { StatusPrinter.printInCaseOfErrorsOrWarnings(loggerContext); } } catch (JoranException e) { throw new IOException("Failed to load log configuration:" + e.getMessage(), e); } } /** * Imports all properties. */ private void importParameters(Map<String,Object> map, ConfigurationProperties properties) { for (String key: properties.stringPropertyNames()) { map.put(key, properties.getProperty(key)); } } /** * Executes a preload batch script, if defined. */ private void executePreload(SystemCell cell) throws URISyntaxException, IOException, CommandException { String preload = _properties.getValue(PROPERTY_DOMAIN_PRELOAD); if (preload != null) { CellShell shell = new CellShell(cell.getNucleus()); importParameters(shell.environment(), _properties); executeBatchFile(shell, _resourceLoader.getResource(preload)); } } /** * Creates a CellShell preloaded with a specific service's * configuration. */ CellShell createShellForService(SystemCell system, ConfigurationProperties properties) { CellShell shell = new CellShell(system.getNucleus()); importParameters(shell.environment(), properties); return shell; } /** * Executes the batch file of the service. */ private void executeService(SystemCell system, ConfigurationProperties properties) throws URISyntaxException, IOException, CommandException { CellShell shell = createShellForService(system, properties); Resource resource = _resourceLoader.getResource(properties.getValue(PROPERTY_DOMAIN_SERVICE_BATCH)); executeBatchFile(shell, resource); } /** * Scans the service directories to locate the service batch file. */ private Resource findBatchFile(ConfigurationProperties properties) throws IOException { String name = properties.getValue(PROPERTY_DOMAIN_SERVICE_URI); if (name == null) { throw new IOException("Property " + PROPERTY_DOMAIN_SERVICE_URI + " is undefined"); } /* Don't search if the URI is absolute. */ URI uri; try { uri = new URI(name); } catch (URISyntaxException e) { throw new IOException(e.getMessage(), e); } if (uri.isAbsolute()) { return _resourceLoader.getResource(name); } /* Search in plugin directories. */ String pluginPath = properties.getValue(PROPERTY_PLUGIN_PATH); for (String dir: pluginPath.split(PATH_DELIMITER)) { File[] files = new File(dir).listFiles(); if (files != null) { for (File plugin: files) { File file = new File(plugin, name); if (file.exists()) { return new FileSystemResource(file); } } } } /* Resolve relative to base URI. */ Resource base = _resourceLoader.getResource(properties.getValue(PROPERTY_DOMAIN_SERVICE_URI_BASE)); return base.createRelative(name); } /** * Executes the batch file in the resource. */ private void executeBatchFile(CellShell shell, Resource resource) throws URISyntaxException, IOException, CommandException { try (InputStream input = resource.getInputStream()) { shell.execute(resource.toString(), new InputStreamReader(input), new Args("")); } } }