| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.hadoop.service.launcher; |
| |
| import org.apache.commons.lang.StringUtils; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.service.Service; |
| import org.apache.hadoop.service.ServiceOperations; |
| import static org.apache.hadoop.test.GenericTestUtils.*; |
| import org.apache.hadoop.util.ExitCodeProvider; |
| import org.apache.hadoop.util.ExitUtil; |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.BeforeClass; |
| import org.junit.Rule; |
| import org.junit.rules.TestName; |
| import org.junit.rules.Timeout; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| public class AbstractServiceLauncherTestBase extends Assert implements |
| LauncherExitCodes { |
| private static final Logger LOG = LoggerFactory.getLogger( |
| AbstractServiceLauncherTestBase.class); |
| public static final String CONF_FILE_DIR = "target/launcher/conf"; |
| |
| /** |
| * A service which will be automatically stopped on teardown. |
| */ |
| private Service serviceToTeardown; |
| |
| /** |
| * All tests have a short life. |
| */ |
| @Rule |
| public Timeout testTimeout = new Timeout(15000); |
| |
| /** |
| * Rule to provide the method name. |
| */ |
| @Rule |
| public TestName methodName = new TestName(); |
| |
| /** |
| * Turn off the exit util JVM exits, downgrading them to exception throws. |
| */ |
| @BeforeClass |
| public static void disableJVMExits() { |
| ExitUtil.disableSystemExit(); |
| ExitUtil.disableSystemHalt(); |
| } |
| |
| /** |
| * rule to name the thread JUnit. |
| */ |
| @Before |
| public void nameThread() { |
| Thread.currentThread().setName("JUnit"); |
| } |
| |
| @After |
| public void stopService() { |
| ServiceOperations.stopQuietly(serviceToTeardown); |
| } |
| |
| public void setServiceToTeardown(Service serviceToTeardown) { |
| this.serviceToTeardown = serviceToTeardown; |
| } |
| |
| /** |
| * Assert that a service is in a state. |
| * @param service service |
| * @param expected expected state |
| */ |
| protected void assertInState(Service service, Service.STATE expected) { |
| assertNotNull(service); |
| Service.STATE actual = service.getServiceState(); |
| failif(actual != expected, |
| "Service %s in state %s expected state: %s", service.getName(), actual, expected); |
| |
| } |
| |
| /** |
| * Assert a service has stopped. |
| * @param service service |
| */ |
| protected void assertStopped(Service service) { |
| assertInState(service, Service.STATE.STOPPED); |
| } |
| |
| /** |
| * Assert that an exception code matches the value expected. |
| * @param expected expected value |
| * @param text text in exception -can be null |
| * @param e exception providing the actual value |
| */ |
| protected void assertExceptionDetails(int expected, |
| String text, |
| ExitCodeProvider e) { |
| assertNotNull(e); |
| String toString = e.toString(); |
| int exitCode = e.getExitCode(); |
| boolean failed = expected != exitCode; |
| failed |= StringUtils.isNotEmpty(text) |
| && !StringUtils.contains(toString, text); |
| failif(failed, |
| "Expected exception with exit code %d and text \"%s\"" |
| + " but got the exit code %d" |
| + " in \"%s\"", |
| expected, text, |
| exitCode, e); |
| } |
| |
| /** |
| * Assert the launch come was a service creation failure. |
| * @param classname argument |
| */ |
| protected void assertServiceCreationFails(String classname) { |
| assertLaunchOutcome(EXIT_SERVICE_CREATION_FAILURE, "", classname); |
| } |
| |
| /** |
| * Assert a launch outcome. |
| * @param expected expected value |
| * @param text text in exception -can be null |
| * @param args CLI args |
| */ |
| protected void assertLaunchOutcome(int expected, |
| String text, |
| String... args) { |
| try { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("Launching service with expected outcome {}", expected); |
| for (String arg : args) { |
| LOG.debug(arg); |
| } |
| } |
| ServiceLauncher.serviceMain(args); |
| } catch (ServiceLaunchException e) { |
| assertExceptionDetails(expected, text, e); |
| } |
| } |
| |
| /** |
| * Assert a launch runs. |
| * @param args CLI args |
| */ |
| protected void assertRuns(String... args) { |
| assertLaunchOutcome(0, "", args); |
| } |
| |
| /** |
| * Init and start a service. |
| * @param service the service |
| * @return the service |
| */ |
| protected <S extends Service> S run(S service) { |
| assertNotNull(service); |
| service.init(new Configuration()); |
| service.start(); |
| return service; |
| } |
| |
| /** |
| * Save a configuration to a config file in the target dir. |
| * @param conf config |
| * @return absolute path |
| * @throws IOException problems |
| */ |
| protected String configFile(Configuration conf) throws IOException { |
| File directory = new File(CONF_FILE_DIR); |
| directory.mkdirs(); |
| File file = File.createTempFile("conf", ".xml", directory); |
| try(OutputStream fos = new FileOutputStream(file)) { |
| conf.writeXml(fos); |
| } |
| return file.getAbsolutePath(); |
| } |
| |
| /** |
| * Create a new config from key-val pairs. |
| * @param kvp a list of key, value, ... |
| * @return a new configuration |
| */ |
| protected Configuration newConf(String... kvp) { |
| int len = kvp.length; |
| assertEquals("unbalanced keypair len of " + len, 0, len % 2); |
| Configuration conf = new Configuration(false); |
| for (int i = 0; i < len; i += 2) { |
| conf.set(kvp[i], kvp[i + 1]); |
| } |
| return conf; |
| } |
| |
| /** varargs to list conversion. */ |
| protected List<String> asList(String... args) { |
| return Arrays.asList(args); |
| } |
| |
| /** |
| * Launch a service with the given list of arguments. Returns |
| * the service launcher, from which the created service can be extracted |
| * via {@link ServiceLauncher#getService()}. |
| * The service is has its execute() method called, but |
| * @param serviceClass service class to create |
| * @param conf configuration |
| * @param args list of arguments |
| * @param execute execute/wait for the service to stop |
| * @param <S> service type |
| * @return the service launcher |
| * @throws ExitUtil.ExitException if the launch's exit code != 0 |
| */ |
| protected <S extends Service> ServiceLauncher<S> launchService( |
| Class serviceClass, |
| Configuration conf, |
| List<String> args, |
| boolean execute) throws ExitUtil.ExitException { |
| ServiceLauncher<S> serviceLauncher = |
| new ServiceLauncher<>(serviceClass.getName()); |
| ExitUtil.ExitException exitException = |
| serviceLauncher.launchService(conf, args, false, execute); |
| if (exitException.getExitCode() == 0) { |
| // success |
| return serviceLauncher; |
| } else { |
| // launch failure |
| throw exitException; |
| } |
| } |
| |
| /** |
| * Launch a service with the given list of arguments. Returns |
| * the service launcher, from which the created service can be extracted. |
| * via {@link ServiceLauncher#getService()}. |
| * |
| * This call DOES NOT call {@link LaunchableService#execute()} or wait for |
| * a simple service to finish. It returns the service that has been created, |
| * initialized and started. |
| * @param serviceClass service class to create |
| * @param conf configuration |
| * @param args varargs launch arguments |
| * @param <S> service type |
| * @return the service launcher |
| * @throws ExitUtil.ExitException if the launch's exit code != 0 |
| */ |
| protected <S extends Service> ServiceLauncher<S> launchService( |
| Class serviceClass, |
| Configuration conf, |
| String... args) throws ExitUtil.ExitException { |
| return launchService(serviceClass, conf, Arrays.asList(args), false); |
| } |
| |
| /** |
| * Launch expecting an exception. |
| * @param serviceClass service class to create |
| * @param conf configuration |
| * @param expectedText expected text; may be "" or null |
| * @param errorCode error code |
| * @param args varargs launch arguments |
| * @return the exception returned if there was a match |
| * @throws AssertionError on a mismatch of expectation and actual |
| */ |
| protected ExitUtil.ExitException launchExpectingException(Class serviceClass, |
| Configuration conf, |
| String expectedText, |
| int errorCode, |
| String... args) { |
| try { |
| ServiceLauncher<Service> launch = launchService(serviceClass, |
| conf, |
| Arrays.asList(args), |
| true); |
| |
| failf("Expected an exception with error code %d and text \"%s\" " |
| + " -but the service completed with :%s", |
| errorCode, expectedText, |
| launch.getServiceException()); |
| return null; |
| } catch (ExitUtil.ExitException e) { |
| int actualCode = e.getExitCode(); |
| boolean condition = errorCode != actualCode || |
| !StringUtils.contains(e.toString(), expectedText); |
| failif(condition, |
| "Expected an exception with error code %d and text \"%s\" " |
| + " -but the service threw an exception with exit code %d: %s", |
| errorCode, expectedText, |
| actualCode, e); |
| |
| return e; |
| } |
| } |
| |
| } |