package org.radargun.service; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import javax.management.MBeanServerConnection; import javax.management.remote.JMXConnector; import org.radargun.Service; import org.radargun.ServiceHelper; import org.radargun.config.Destroy; import org.radargun.config.Init; import org.radargun.config.Property; import org.radargun.logging.Log; import org.radargun.logging.LogFactory; import org.radargun.traits.JmxConnectionProvider; import org.radargun.traits.ProvidesTrait; import org.radargun.utils.TimeConverter; import org.radargun.utils.Utils; /** * @author Radim Vansa <rvansa@redhat.com> */ @Service(doc = InfinispanServerService.SERVICE_DESCRIPTION) public class InfinispanServerService extends JavaProcessService { protected static final String SERVICE_DESCRIPTION = "Service running Infinispan Server"; protected static final String JBOSS_HOME = "JBOSS_HOME"; protected final Log log = LogFactory.getLog(getClass()); @Property(doc = "Home directory for the server") private String home; @Property(doc = "Server zip. If set, the zip will be extracted to the 'home' directory.") private String serverZip; @Property(doc = "Directory for storing logs") private String logDir; @Property(doc = "Is this executed on Windows (should we use *.bat instead of *.sh)? Default is false.") private boolean windows = false; @Property(doc = "JMX domain. Default is 'org.infinispan'.") protected String jmxDomain = "org.infinispan"; @Property(doc = "Name of the cache manager/cache container. Default is 'default'.") protected String cacheManagerName = "default"; @Property(doc = "Start thread periodically dumping JGroups JMX data. Default is false.") protected boolean jgroupsDumperEnabled = false; @Property(doc = "Period in which should be JGroups JMX data dumped. Default is 10 seconds.", converter = TimeConverter.class) protected long jgroupsDumpPeriod = 10000; @Property(doc = "Period for checking current membership. Default is 10 seconds.", converter = TimeConverter.class) protected long viewCheckPeriod = 10000; @Property(doc = "Number of threads in scheduled tasks pool. Default is 2.") protected int executorPoolSize = 2; protected ScheduledExecutorService executor; protected InfinispanServerClustered clustered; protected volatile MBeanServerConnection connection; @Init public void init() { executor = new ScheduledThreadPoolExecutor(executorPoolSize); lifecycle = new InfinispanServerLifecycle(this); clustered = new InfinispanServerClustered(this); try { if (serverZip != null) { Utils.unzip(serverZip, home); Path homePath = FileSystems.getDefault().getPath(home); List<Path> homeContents = Files.list(homePath).collect(Collectors.toList()); // move the server files 1 directory up (remove the extra directory) if (homeContents.size() == 1) { Path fromDir = homeContents.get(0); for (Path path : Files.list(fromDir).collect(Collectors.toList())) { Files.move(path, FileSystems.getDefault().getPath(home, path.getFileName().toString())); } Files.delete(fromDir); } // the extraction erases the executable bits Utils.setPermissions(FileSystems.getDefault().getPath(home, "bin", getStartScriptPrefix() + (windows ? "bat" : "sh")).toString(), "rwxr-xr-x"); } URL resource = getClass().getResource("/" + file); Path filesystemFile = FileSystems.getDefault().getPath(file); Path target = FileSystems.getDefault().getPath(home, "standalone", "configuration", "radargun-" + ServiceHelper.getSlaveIndex() + ".xml"); if (resource != null) { try (InputStream is = resource.openStream()) { log.info("Found " + file + " as a resource"); Files.copy(is, target, StandardCopyOption.REPLACE_EXISTING); } } else if (filesystemFile.toFile().exists()) { log.info("Found " + file + " in plugin directory"); Files.copy(filesystemFile, target, StandardCopyOption.REPLACE_EXISTING); } else if (FileSystems.getDefault().getPath(home, "standalone", "configuration", file).toFile().exists()) { log.info("Found " + file + " in server configuration directory"); filesystemFile = FileSystems.getDefault().getPath(home, "standalone", "configuration", file); // Set the file variable to the full path to the file to satisfy AbstractConfigurationProvider file = filesystemFile.toString(); Files.copy(filesystemFile, target, StandardCopyOption.REPLACE_EXISTING); } else { throw new FileNotFoundException("File " + file + " not found neither as resource not in filesystem."); } } catch (IOException e) { log.error("Failed to prepare the server directory", e); throw new RuntimeException(e); } lifecycle.addListener(new ProcessLifecycle.ListenerAdapter() { private JMXConnector connector; @Override public void afterStart() { JmxConnectionProvider connectionProvider = createConnectionProvider(); if (connectionProvider == null) { return; } connector = connectionProvider.getConnector(); try { connection = connector.getMBeanServerConnection(); } catch (IOException e) { log.error("Failed to open MBean connection", e); } } @Override public void beforeStop(boolean graceful) { connection = null; try { if (connector != null) connector.close(); } catch (IOException e) { log.error("Failed to close JMX connector", e); } } }); if (jgroupsDumperEnabled) { schedule(new ServerJGroupsDumper(this), jgroupsDumpPeriod); } } @Destroy public void destroy() { Utils.shutdownAndWait(executor); } @ProvidesTrait public ServerConfigurationProvider createServerConfigurationProvider() { return new ServerConfigurationProvider(this); } @ProvidesTrait public InfinispanServerClustered getClustered() { return clustered; } @Override protected List<String> getCommand() { ArrayList<String> command = new ArrayList<String>(); command.add(FileSystems.getDefault().getPath(home, "bin", getStartScriptPrefix() + (windows ? "bat" : "sh")).toString()); command.add("-Djboss.node.name=slave" + ServiceHelper.getSlaveIndex()); if (logDir != null) { command.add("-Djboss.server.log.dir=" + logDir); } command.addAll(args); command.add("-server-config"); command.add("radargun-" + ServiceHelper.getSlaveIndex() + ".xml"); return command; } protected String getStartScriptPrefix() { return "clustered."; } @Override public Map<String, String> getEnvironment() { Map<String, String> envs = super.getEnvironment(); envs.put(JBOSS_HOME, home); return envs; } protected void schedule(Runnable task, long period) { executor.scheduleAtFixedRate(task, 0, period, TimeUnit.MILLISECONDS); } }