/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library 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 2.1 of the License, or (at your option) * any later version. * * This library 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. */ package com.liferay.gradle.plugins.test.integration; import com.liferay.gradle.plugins.test.integration.internal.util.GradleUtil; import com.liferay.gradle.plugins.test.integration.internal.util.StringUtil; import com.liferay.gradle.plugins.test.integration.tasks.BaseAppServerTask; import com.liferay.gradle.plugins.test.integration.tasks.JmxRemotePortSpec; import com.liferay.gradle.plugins.test.integration.tasks.ManagerSpec; import com.liferay.gradle.plugins.test.integration.tasks.ModuleFrameworkBaseDirSpec; import com.liferay.gradle.plugins.test.integration.tasks.SetUpArquillianTask; import com.liferay.gradle.plugins.test.integration.tasks.SetUpTestableTomcatTask; import com.liferay.gradle.plugins.test.integration.tasks.StartTestableTomcatTask; import com.liferay.gradle.plugins.test.integration.tasks.StopTestableTomcatTask; import com.liferay.gradle.util.FileUtil; import com.liferay.gradle.util.OSDetector; import com.liferay.gradle.util.copy.RenameDependencyClosure; import groovy.lang.Closure; import java.io.File; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.locks.ReentrantLock; import org.gradle.StartParameter; import org.gradle.api.Action; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.DependencySet; import org.gradle.api.execution.TaskExecutionGraph; import org.gradle.api.file.FileCopyDetails; import org.gradle.api.file.FileTree; import org.gradle.api.file.SourceDirectorySet; import org.gradle.api.invocation.Gradle; import org.gradle.api.logging.Logger; import org.gradle.api.plugins.JavaBasePlugin; import org.gradle.api.plugins.PluginContainer; import org.gradle.api.plugins.WarPlugin; import org.gradle.api.specs.Spec; import org.gradle.api.tasks.Copy; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.StopExecutionException; import org.gradle.api.tasks.testing.Test; import org.gradle.process.JavaForkOptions; /** * @author Andrea Di Giorgi */ public class TestIntegrationPlugin implements Plugin<Project> { public static final String COPY_TEST_MODULES_TASK_NAME = "copyTestModules"; public static final String PLUGIN_NAME = "testIntegration"; public static final String SET_UP_ARQUILLIAN_TASK_NAME = "setUpArquillian"; public static final String SET_UP_TESTABLE_TOMCAT_TASK_NAME = "setUpTestableTomcat"; public static final String START_TESTABLE_TOMCAT_TASK_NAME = "startTestableTomcat"; public static final String STOP_TESTABLE_TOMCAT_TASK_NAME = "stopTestableTomcat"; public static final String TEST_MODULES_CONFIGURATION_NAME = "testModules"; @Override public void apply(final Project project) { GradleUtil.applyPlugin(project, TestIntegrationBasePlugin.class); final SourceSet testIntegrationSourceSet = GradleUtil.getSourceSet( project, TestIntegrationBasePlugin.TEST_INTEGRATION_SOURCE_SET_NAME); final Test testIntegrationTask = (Test)GradleUtil.getTask( project, TestIntegrationBasePlugin.TEST_INTEGRATION_TASK_NAME); final TestIntegrationTomcatExtension testIntegrationTomcatExtension = GradleUtil.addExtension( project, PLUGIN_NAME + "Tomcat", TestIntegrationTomcatExtension.class); Configuration testModulesConfiguration = _addConfigurationTestModules( project); Copy copyTestModulesTask = _addTaskCopyTestModules( project, testModulesConfiguration, testIntegrationTomcatExtension); SetUpTestableTomcatTask setUpTestableTomcatTask = _addTaskSetUpTestableTomcat( project, copyTestModulesTask, testIntegrationTomcatExtension); StopTestableTomcatTask stopTestableTomcatTask = _addTaskStopTestableTomcat( project, testIntegrationTask, testIntegrationTomcatExtension); StartTestableTomcatTask startTestableTomcatTask = _addTaskStartTestableTomcat( project, setUpTestableTomcatTask, stopTestableTomcatTask, testIntegrationTomcatExtension); PluginContainer pluginContainer = project.getPlugins(); pluginContainer.withType( WarPlugin.class, new Action<WarPlugin>() { @Override public void execute(WarPlugin warPlugin) { SetUpArquillianTask setUpArquillianTask = _addTaskSetUpArquillian( project, testIntegrationSourceSet, testIntegrationTomcatExtension); testIntegrationTask.dependsOn(setUpArquillianTask); } }); _configureTaskTestIntegration( testIntegrationTask, testIntegrationSourceSet, testIntegrationTomcatExtension, startTestableTomcatTask); } private static int _updateStartedAppServerStopCounters( File binDir, boolean increment) { int originalCounter = 0; if (_startedAppServerStopCounters.containsKey(binDir)) { originalCounter = _startedAppServerStopCounters.get(binDir); } int counter = originalCounter; if (increment) { counter++; } else { counter--; } _startedAppServerStopCounters.put(binDir, counter); return originalCounter; } private Configuration _addConfigurationTestModules(final Project project) { Configuration configuration = GradleUtil.addConfiguration( project, TEST_MODULES_CONFIGURATION_NAME); configuration.defaultDependencies( new Action<DependencySet>() { @Override public void execute(DependencySet dependencySet) { _addDependenciesTestModules(project); } }); configuration.setDescription( "Configures additional OSGi modules to deploy during integration " + "testing."); configuration.setVisible(false); return configuration; } private void _addDependenciesTestModules(Project project) { GradleUtil.addDependency( project, TEST_MODULES_CONFIGURATION_NAME, "org.apache.aries.jmx", "org.apache.aries.jmx.core", "1.1.7"); } private Copy _addTaskCopyTestModules( Project project, Configuration testModulesConfiguration, final TestIntegrationTomcatExtension testIntegrationTomcatExtension) { final Copy copy = GradleUtil.addTask( project, COPY_TEST_MODULES_TASK_NAME, Copy.class); final RenameDependencyClosure renameDependencyClosure = new RenameDependencyClosure( project, testModulesConfiguration.getName()); copy.eachFile( new Action<FileCopyDetails>() { @Override public void execute(FileCopyDetails fileCopyDetails) { String fileName = renameDependencyClosure.call( fileCopyDetails.getName()); File file = new File(copy.getDestinationDir(), fileName); if (file.exists()) { fileCopyDetails.exclude(); } } }); copy.from(testModulesConfiguration); copy.into( new Callable<File>() { @Override public File call() throws Exception { return new File( testIntegrationTomcatExtension. getModuleFrameworkBaseDir(), "test"); } }); copy.rename(renameDependencyClosure); copy.setDescription( "Copies additional OSGi modules to deploy during integration " + "testing."); return copy; } private SetUpArquillianTask _addTaskSetUpArquillian( final Project project, final SourceSet testIntegrationSourceSet, TestIntegrationTomcatExtension testIntegrationTomcatExtension) { SetUpArquillianTask setUpArquillianTask = GradleUtil.addTask( project, SET_UP_ARQUILLIAN_TASK_NAME, SetUpArquillianTask.class); setUpArquillianTask.setDescription( "Creates the Arquillian container configuration file for this " + "project."); setUpArquillianTask.setOutputDir( new Callable<File>() { @Override public File call() throws Exception { return _getSrcDir(testIntegrationSourceSet.getResources()); } }); _configureJmxRemotePortSpec( setUpArquillianTask, testIntegrationTomcatExtension); _configureManagerSpec( setUpArquillianTask, testIntegrationTomcatExtension); return setUpArquillianTask; } private SetUpTestableTomcatTask _addTaskSetUpTestableTomcat( Project project, Copy copyTestModulesTask, final TestIntegrationTomcatExtension testIntegrationTomcatExtension) { final SetUpTestableTomcatTask setUpTestableTomcatTask = GradleUtil.addTask( project, SET_UP_TESTABLE_TOMCAT_TASK_NAME, SetUpTestableTomcatTask.class); setUpTestableTomcatTask.dependsOn(copyTestModulesTask); setUpTestableTomcatTask.onlyIf( new Spec<Task>() { @Override public boolean isSatisfiedBy(Task task) { _startedAppServersReentrantLock.lock(); try { if (_startedAppServerBinDirs.contains( setUpTestableTomcatTask.getBinDir())) { return false; } return true; } finally { _startedAppServersReentrantLock.unlock(); } } }); setUpTestableTomcatTask.setDescription( "Configures the local Liferay Tomcat bundle to run integration " + "tests."); setUpTestableTomcatTask.setDir( new Callable<File>() { @Override public File call() throws Exception { return testIntegrationTomcatExtension.getDir(); } }); _configureJmxRemotePortSpec( setUpTestableTomcatTask, testIntegrationTomcatExtension); _configureManagerSpec( setUpTestableTomcatTask, testIntegrationTomcatExtension); _configureModuleFrameworkBaseDirSpec( setUpTestableTomcatTask, testIntegrationTomcatExtension); return setUpTestableTomcatTask; } private StartTestableTomcatTask _addTaskStartTestableTomcat( Project project, SetUpTestableTomcatTask setUpTestableTomcatTask, StopTestableTomcatTask stopTestableTomcatTask, final TestIntegrationTomcatExtension testIntegrationTomcatExtension) { StartTestableTomcatTask startTestableTomcatTask = GradleUtil.addTask( project, START_TESTABLE_TOMCAT_TASK_NAME, StartTestableTomcatTask.class); startTestableTomcatTask.dependsOn(setUpTestableTomcatTask); Action<Task> action = new Action<Task>() { @Override public void execute(Task task) { StartTestableTomcatTask startTestableTomcatTask = (StartTestableTomcatTask)task; File binDir = startTestableTomcatTask.getBinDir(); Logger logger = startTestableTomcatTask.getLogger(); boolean started = false; _startedAppServersReentrantLock.lock(); try { if (_startedAppServerBinDirs.contains(binDir)) { started = true; } else { _startedAppServerBinDirs.add(binDir); } } finally { _startedAppServersReentrantLock.unlock(); } if (started) { if (logger.isDebugEnabled()) { logger.debug( "Application server {} is already started", binDir); } Project project = startTestableTomcatTask.getProject(); Gradle gradle = project.getGradle(); StartParameter startParameter = gradle.getStartParameter(); if (startParameter.isParallelProjectExecutionEnabled()) { if (logger.isDebugEnabled()) { logger.debug( "Waiting for application server {} to be " + "reachable", binDir); } startTestableTomcatTask.waitForReachable(); } throw new StopExecutionException(); } } }; startTestableTomcatTask.doFirst(action); startTestableTomcatTask.finalizedBy(stopTestableTomcatTask); startTestableTomcatTask.onlyIf( new Spec<Task>() { @Override public boolean isSatisfiedBy(Task task) { StartTestableTomcatTask startTestableTomcatTask = (StartTestableTomcatTask)task; if (startTestableTomcatTask.isReachable()) { return false; } return true; } }); startTestableTomcatTask.setDescription( "Starts the local Liferay Tomcat bundle."); startTestableTomcatTask.setExecutable( _getTomcatExecutableFileName("catalina")); startTestableTomcatTask.setExecutableArgs(Collections.singleton("run")); startTestableTomcatTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP); startTestableTomcatTask.setLiferayHome( new Callable<File>() { @Override public File call() throws Exception { return testIntegrationTomcatExtension.getLiferayHome(); } }); _configureBaseAppServerTask( startTestableTomcatTask, testIntegrationTomcatExtension); return startTestableTomcatTask; } private StopTestableTomcatTask _addTaskStopTestableTomcat( Project project, Test testIntegrationTask, TestIntegrationTomcatExtension testIntegrationTomcatExtension) { final StopTestableTomcatTask stopTestableTomcatTask = GradleUtil.addTask( project, STOP_TESTABLE_TOMCAT_TASK_NAME, StopTestableTomcatTask.class); Action<Task> action = new Action<Task>() { @Override public void execute(Task task) { StopTestableTomcatTask stopTestableTomcatTask = (StopTestableTomcatTask)task; File binDir = stopTestableTomcatTask.getBinDir(); Logger logger = stopTestableTomcatTask.getLogger(); _startedAppServersReentrantLock.lock(); try { if (!_startedAppServerBinDirs.contains(binDir)) { if (logger.isDebugEnabled()) { logger.debug( "Application server {} is already stopped", binDir); } throw new StopExecutionException(); } int originalCounter = _updateStartedAppServerStopCounters( binDir, false); if (originalCounter > 1) { if (logger.isDebugEnabled()) { logger.debug( "Application server {} cannot be stopped " + "now, still {} to execute", binDir, originalCounter - 1); } throw new StopExecutionException(); } } finally { _startedAppServersReentrantLock.unlock(); } } }; stopTestableTomcatTask.doFirst(action); action = new Action<Task>() { @Override public void execute(Task task) { StopTestableTomcatTask setUpTestableTomcatTask = (StopTestableTomcatTask)task; _startedAppServersReentrantLock.lock(); try { _startedAppServerBinDirs.remove( setUpTestableTomcatTask.getBinDir()); } finally { _startedAppServersReentrantLock.unlock(); } } }; stopTestableTomcatTask.doLast(action); stopTestableTomcatTask.mustRunAfter(testIntegrationTask); stopTestableTomcatTask.setDescription( "Stops the local Liferay Tomcat bundle."); stopTestableTomcatTask.setExecutable( _getTomcatExecutableFileName("shutdown")); stopTestableTomcatTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP); _configureBaseAppServerTask( stopTestableTomcatTask, testIntegrationTomcatExtension); _configureModuleFrameworkBaseDirSpec( stopTestableTomcatTask, testIntegrationTomcatExtension); Gradle gradle = project.getGradle(); TaskExecutionGraph taskExecutionGraph = gradle.getTaskGraph(); Closure<Void> closure = new Closure<Void>(gradle) { @SuppressWarnings("unused") public void doCall(TaskExecutionGraph taskExecutionGraph) { if (taskExecutionGraph.hasTask(stopTestableTomcatTask)) { _startedAppServersReentrantLock.lock(); try { _updateStartedAppServerStopCounters( stopTestableTomcatTask.getBinDir(), true); } finally { _startedAppServersReentrantLock.unlock(); } } } }; taskExecutionGraph.whenReady(closure); return stopTestableTomcatTask; } private void _configureBaseAppServerTask( BaseAppServerTask baseAppServerTask, final TestIntegrationTomcatExtension testIntegrationTomcatExtension) { baseAppServerTask.setBinDir( new Callable<File>() { @Override public File call() throws Exception { return new File( testIntegrationTomcatExtension.getDir(), "bin"); } }); baseAppServerTask.setCheckPath( new Callable<String>() { @Override public String call() throws Exception { return testIntegrationTomcatExtension.getCheckPath(); } }); baseAppServerTask.setPortNumber( new Callable<Integer>() { @Override public Integer call() throws Exception { return testIntegrationTomcatExtension.getPortNumber(); } }); } private void _configureJmxRemotePortSpec( JmxRemotePortSpec jmxRemotePortSpec, final TestIntegrationTomcatExtension testIntegrationTomcatExtension) { jmxRemotePortSpec.setJmxRemotePort( new Callable<Integer>() { @Override public Integer call() throws Exception { return testIntegrationTomcatExtension.getJmxRemotePort(); } }); } private void _configureManagerSpec( ManagerSpec managerSpec, final TestIntegrationTomcatExtension testIntegrationTomcatExtension) { managerSpec.setManagerPassword( new Callable<String>() { @Override public String call() throws Exception { return testIntegrationTomcatExtension.getManagerPassword(); } }); managerSpec.setManagerUserName( new Callable<String>() { @Override public String call() throws Exception { return testIntegrationTomcatExtension.getManagerUserName(); } }); } private void _configureModuleFrameworkBaseDirSpec( ModuleFrameworkBaseDirSpec moduleFrameworkBaseDirSpec, final TestIntegrationTomcatExtension testIntegrationTomcatExtension) { moduleFrameworkBaseDirSpec.setModuleFrameworkBaseDir( new Callable<File>() { @Override public File call() throws Exception { return testIntegrationTomcatExtension. getModuleFrameworkBaseDir(); } }); } private void _configureTaskSystemProperty( JavaForkOptions javaForkOptions, String key, File file) { Map<String, Object> systemProperties = javaForkOptions.getSystemProperties(); if (!systemProperties.containsKey(key)) { systemProperties.put(key, FileUtil.getAbsolutePath(file)); } } private void _configureTaskTestIntegration( final Test test, final SourceSet testIntegrationSourceSet, final TestIntegrationTomcatExtension testIntegrationTomcatExtension, final StartTestableTomcatTask startTestableTomcatTask) { Closure<Task> closure = new Closure<Task>(test.getProject()) { @SuppressWarnings("unused") public Task doCall(Test test) { SourceDirectorySet sourceDirectorySet = testIntegrationSourceSet.getResources(); for (File dir : sourceDirectorySet.getSrcDirs()) { File file = new File( dir, _SKIP_MANAGED_APP_SERVER_FILE_NAME); if (file.exists()) { return null; } } return startTestableTomcatTask; } }; test.dependsOn(closure); test.jvmArgs( "-Djava.net.preferIPv4Stack=true", "-Dliferay.mode=test", "-Duser.timezone=GMT"); Project project = test.getProject(); project.afterEvaluate( new Action<Project>() { @Override public void execute(Project project) { _configureTaskTestIntegrationEnabled( test, testIntegrationSourceSet); // GRADLE-2697 _configureTaskSystemProperty( test, "app.server.tomcat.dir", testIntegrationTomcatExtension.getDir()); } }); } private void _configureTaskTestIntegrationEnabled( Test test, SourceSet testIntegrationSourceSet) { Project project = test.getProject(); Map<String, Object> args = new HashMap<>(); args.put( "excludes", StringUtil.replaceEnding(test.getExcludes(), ".class", ".java")); args.put( "includes", StringUtil.replaceEnding(test.getIncludes(), ".class", ".java")); SourceDirectorySet sourceDirectorySet = testIntegrationSourceSet.getJava(); for (File dir : sourceDirectorySet.getSrcDirs()) { args.put("dir", dir); FileTree fileTree = project.fileTree(args); if (!fileTree.isEmpty()) { return; } } test.setDependsOn(Collections.emptySet()); test.setEnabled(false); } private File _getSrcDir(SourceDirectorySet sourceDirectorySet) { Set<File> srcDirs = sourceDirectorySet.getSrcDirs(); Iterator<File> iterator = srcDirs.iterator(); return iterator.next(); } private String _getTomcatExecutableFileName(String fileName) { if (OSDetector.isWindows()) { fileName += ".bat"; } return fileName; } private static final String _SKIP_MANAGED_APP_SERVER_FILE_NAME = "skip.managed.app.server"; private static final Set<File> _startedAppServerBinDirs = new HashSet<>(); private static final ReentrantLock _startedAppServersReentrantLock = new ReentrantLock(); private static final Map<File, Integer> _startedAppServerStopCounters = new HashMap<>(); }