/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.inject; import com.google.common.io.Files; import com.google.inject.Inject; import com.google.inject.Injector; import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.commons.lang.IoUtil; import org.eclipse.che.commons.lang.Pair; import org.eclipse.che.commons.test.SystemPropertiesHelper; import org.mockito.ArgumentCaptor; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.inject.Named; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import java.io.File; import java.io.IOException; import java.io.Writer; import java.net.URI; import java.net.URL; import java.nio.charset.Charset; import java.util.Map; import java.util.Properties; import static java.io.File.pathSeparator; import static java.util.Collections.emptyEnumeration; import static org.eclipse.che.commons.test.SystemPropertiesHelper.overrideSystemProperties; import static org.eclipse.che.inject.CheBootstrap.CHE_LOCAL_CONF_DIR; import static org.eclipse.che.inject.CheBootstrap.PROPERTIES_ALIASES_CONFIG_FILE; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; public class CheBootstrapTest { private CheBootstrap cheBootstrap; private ServletContext servletContext; private File che; private File userCongDir; private SystemPropertiesHelper systemPropertiesHelper; @BeforeMethod public void setUp() throws Exception { systemPropertiesHelper = overrideSystemProperties(); cheBootstrap = new CheBootstrap(); servletContext = mock(ServletContext.class); URL classesDirUrl = Thread.currentThread().getContextClassLoader().getResource("."); File classesDir = new File(classesDirUrl.toURI()); che = new File(classesDir, "che"); che.mkdir(); userCongDir = new File(System.getenv(CHE_LOCAL_CONF_DIR)); userCongDir.mkdirs(); mockServletContext(); } @AfterMethod public void tearDown() throws Exception { try { cheBootstrap.contextDestroyed(new ServletContextEvent(servletContext)); } catch (Throwable ignored) { } systemPropertiesHelper.restoreFromBackup(); IoUtil.deleteRecursive(che); IoUtil.deleteRecursive(userCongDir); File aliases = new File(che.getParent(), PROPERTIES_ALIASES_CONFIG_FILE); if (aliases.exists()) { aliases.delete(); } ModuleScanner.modules.clear(); } private Properties createTestProperties() { final Properties properties = new Properties(); properties.put("test_int", "123"); properties.put("test_bool", "true"); properties.put("test_uri", "file:/a/b/c"); properties.put("test_url", "http://localhost"); properties.put("test_file", "/a/b/c"); properties.put("test_strings", "a, b, c"); properties.put("test_pair_of_strings", "a=b"); properties.put("test_pair_of_strings2", "a"); properties.put("test_pair_of_strings3", "a="); properties.put("test_pair_array", "a=b,c=d"); properties.put("some.dir.in_tmp_dir", "${java.io.tmpdir}/some_dir"); properties.put("suffixed.PATH", "${PATH}" + pathSeparator + "some_path"); properties.put("nullable", "NULL"); return properties; } @Test public void readsConfigurationPropertiesFromCheDirectory() throws Exception { writePropertiesFile(che, "che.properties", createTestProperties()); ModuleScanner.modules.add(binder -> binder.bind(TestChePropertiesComponent.class)); cheBootstrap.contextInitialized(new ServletContextEvent(servletContext)); Injector injector = retrieveComponentFromServletContext(Injector.class); TestChePropertiesComponent testComponent = injector.getInstance(TestChePropertiesComponent.class); assertEquals(testComponent.parameter_pair, Pair.of("a", "b")); assertEquals(testComponent.parameter_pair2, Pair.of("a", (String)null)); assertEquals(testComponent.parameter_pair3, Pair.of("a", "")); assertEquals(testComponent.parameter_pair_array, new Pair[]{Pair.of("a", "b"), Pair.of("c", "d")}); assertEquals(testComponent.nullable, null); assertEquals(testComponent.parameter_uri, new URI("file:/a/b/c")); assertEquals(testComponent.parameter_url, new URL("http://localhost")); assertEquals(testComponent.parameter_file, new File("/a/b/c")); assertEquals(testComponent.parameter_strings, new String[]{"a", "b", "c"}); assertEquals(testComponent.parameter_int, 123); assertEquals(testComponent.parameter_long, 123); assertEquals(testComponent.parameter_bool, true); assertEquals(testComponent.someDir, new File(System.getProperty("java.io.tmpdir"), "/some_dir")); assertEquals(testComponent.suffixedPath, System.getenv("PATH") + pathSeparator + "some_path"); } @Test public void readsConfigurationPropertiesFromSystemProperties() throws Exception { setSystemProperties(createTestProperties()); ModuleScanner.modules.add(binder -> binder.bind(TestSystemPropertiesComponent.class)); cheBootstrap.contextInitialized(new ServletContextEvent(servletContext)); Injector injector = retrieveComponentFromServletContext(Injector.class); TestSystemPropertiesComponent testComponent = injector.getInstance(TestSystemPropertiesComponent.class); assertEquals(testComponent.parameter_pair, Pair.of("a", "b")); assertEquals(testComponent.parameter_pair2, Pair.of("a", (String)null)); assertEquals(testComponent.parameter_pair3, Pair.of("a", "")); assertEquals(testComponent.parameter_pair_array, new Pair[]{Pair.of("a", "b"), Pair.of("c", "d")}); assertEquals(testComponent.nullable, null); assertEquals(testComponent.parameter_uri, new URI("file:/a/b/c")); assertEquals(testComponent.parameter_url, new URL("http://localhost")); assertEquals(testComponent.parameter_file, new File("/a/b/c")); assertEquals(testComponent.parameter_strings, new String[]{"a", "b", "c"}); assertEquals(testComponent.parameter_int, 123); assertEquals(testComponent.parameter_long, 123); assertEquals(testComponent.parameter_bool, true); assertEquals(testComponent.someDir, new File(System.getProperty("java.io.tmpdir"), "/some_dir")); assertEquals(testComponent.suffixedPath, System.getenv("PATH") + pathSeparator + "some_path"); } @Test public void readsConfigurationPropertiesFromEnvProperties() throws Exception { ModuleScanner.modules.add(binder -> binder.bind(TestEnvPropertiesComponent.class)); cheBootstrap.contextInitialized(new ServletContextEvent(servletContext)); Injector injector = retrieveComponentFromServletContext(Injector.class); TestEnvPropertiesComponent testComponent = injector.getInstance(TestEnvPropertiesComponent.class); assertEquals(testComponent.string, System.getenv("PATH")); } @Test public void propertiesFromUserSpecifiedLocationOverrideCheProperties() throws Exception { systemPropertiesHelper.property(CHE_LOCAL_CONF_DIR, userCongDir.getAbsolutePath()); Properties cheProperties = new Properties(); cheProperties.put("che.some.name", "che_value"); writePropertiesFile(che, "che.properties", cheProperties); Properties userProperties = new Properties(); userProperties.put("che.some.name", "user_value"); writePropertiesFile(userCongDir, "user.properties", userProperties); ModuleScanner.modules.add(binder -> binder.bind(TestConfOverrideComponent.class)); cheBootstrap.contextInitialized(new ServletContextEvent(servletContext)); Injector injector = retrieveComponentFromServletContext(Injector.class); TestConfOverrideComponent testComponent = injector.getInstance(TestConfOverrideComponent.class); assertEquals(testComponent.string, "user_value"); } @Test public void system_properties_prefixed_with_che_dot_override_user_specified_and_che_properties() throws Exception { Properties cheProperties = new Properties(); cheProperties.put("che.some.name", "che_value"); cheProperties.put("che.some.other.name", "NULL"); writePropertiesFile(che, "che.properties", cheProperties); Properties userProperties = new Properties(); userProperties.put("che.some.name", "user_value"); writePropertiesFile(userCongDir, "user.properties", userProperties); systemPropertiesHelper.property("che.some.name", "che_dot_system_property_value"); ModuleScanner.modules.add(binder -> binder.bind(TestConfOverrideComponent.class)); cheBootstrap.contextInitialized(new ServletContextEvent(servletContext)); Injector injector = retrieveComponentFromServletContext(Injector.class); TestConfOverrideComponent testComponent = injector.getInstance(TestConfOverrideComponent.class); assertEquals(testComponent.string, "che_dot_system_property_value"); } @Test public void environment_variables_prefixed_with_che_underscore_override_che_dot_prefixed_system_and_user_specified_and_che_properties() throws Exception { Properties cheProperties = new Properties(); cheProperties.put("che.some.other.name", "che_value"); cheProperties.put("che.some.name", "NULL"); writePropertiesFile(che, "che.properties", cheProperties); Properties userProperties = new Properties(); userProperties.put("che.some.other.name", "user_value"); writePropertiesFile(userCongDir, "user.properties", userProperties); systemPropertiesHelper.property("che.some.other.name", "che_dot_system_property_value"); ModuleScanner.modules.add(binder -> binder.bind(TestConfOverrideComponent.class)); cheBootstrap.contextInitialized(new ServletContextEvent(servletContext)); Injector injector = retrieveComponentFromServletContext(Injector.class); TestConfOverrideComponent testComponent = injector.getInstance(TestConfOverrideComponent.class); assertEquals(testComponent.otherString, System.getenv("CHE_SOME_OTHER_NAME")); } @Test public void environment_variables_prefixed_with_che_underscore_convert_double_underscores_into_one_underscore_in_variable_name() throws Exception { Properties cheProperties = new Properties(); cheProperties.put("che.some.other.name_with_underscores", "che_value"); cheProperties.put("che.some.name", "NULL"); writePropertiesFile(che, "che.properties", cheProperties); Properties userProperties = new Properties(); userProperties.put("che.some.other.name_with_underscores", "user_value"); writePropertiesFile(userCongDir, "user.properties", userProperties); systemPropertiesHelper.property("che.some.other.name_with_underscores", "che_dot_system_property_value"); ModuleScanner.modules.add(binder -> binder.bind(TestConfOverrideWithUnderscoresComponent.class)); cheBootstrap.contextInitialized(new ServletContextEvent(servletContext)); Injector injector = retrieveComponentFromServletContext(Injector.class); TestConfOverrideWithUnderscoresComponent testComponent = injector.getInstance(TestConfOverrideWithUnderscoresComponent.class); assertEquals(testComponent.otherString, System.getenv("CHE_SOME_OTHER_NAME__WITH__UNDERSCORES")); } @Test public void processesPropertyAliases() throws Exception { Properties cheProperties = new Properties(); cheProperties.put("very.new.some.name", "some_value"); writePropertiesFile(che, "che.properties", cheProperties); Properties aliases = new Properties(); aliases.put("very.new.some.name", "new.some.name, che.some.name"); writePropertiesFile(che.getParentFile(), PROPERTIES_ALIASES_CONFIG_FILE, aliases); ModuleScanner.modules.add(binder -> binder.bind(TestConfAliasComponent.class)); cheBootstrap.contextInitialized(new ServletContextEvent(servletContext)); Injector injector = retrieveComponentFromServletContext(Injector.class); TestConfAliasComponent testComponent = injector.getInstance(TestConfAliasComponent.class); assertEquals(testComponent.string, "some_value"); assertEquals(testComponent.otherString, "some_value"); assertEquals(testComponent.otherOtherString, "some_value"); } static class TestChePropertiesComponent { @Named("test_int") @Inject int parameter_int; @Named("test_int") @Inject int parameter_long; @Named("test_bool") @Inject boolean parameter_bool; @Named("test_uri") @Inject URI parameter_uri; @Named("test_url") @Inject URL parameter_url; @Named("test_file") @Inject File parameter_file; @Named("test_strings") @Inject String[] parameter_strings; @Named("test_pair_of_strings") @Inject Pair<String, String> parameter_pair; @Named("test_pair_of_strings2") @Inject Pair<String, String> parameter_pair2; @Named("test_pair_of_strings3") @Inject Pair<String, String> parameter_pair3; @Named("test_pair_array") @Inject Pair<String, String>[] parameter_pair_array; @Named("some.dir.in_tmp_dir") @Inject File someDir; @Named("suffixed.PATH") @Inject String suffixedPath; @Named("nullable") @Inject @Nullable String nullable; } static class TestSystemPropertiesComponent { @Named("sys.test_int") @Inject int parameter_int; @Named("sys.test_int") @Inject int parameter_long; @Named("sys.test_bool") @Inject boolean parameter_bool; @Named("sys.test_uri") @Inject URI parameter_uri; @Named("sys.test_url") @Inject URL parameter_url; @Named("sys.test_file") @Inject File parameter_file; @Named("sys.test_strings") @Inject String[] parameter_strings; @Named("sys.test_pair_of_strings") @Inject Pair<String, String> parameter_pair; @Named("sys.test_pair_of_strings2") @Inject Pair<String, String> parameter_pair2; @Named("sys.test_pair_of_strings3") @Inject Pair<String, String> parameter_pair3; @Named("sys.test_pair_array") @Inject Pair<String, String>[] parameter_pair_array; @Named("sys.some.dir.in_tmp_dir") @Inject File someDir; @Named("sys.suffixed.PATH") @Inject String suffixedPath; @Named("sys.nullable") @Inject @Nullable String nullable; } static class TestEnvPropertiesComponent { @Named("env.PATH") @Inject String string; } static class TestConfOverrideComponent { @Named("che.some.name") @Inject @Nullable String string; @Named("che.some.other.name") @Inject @Nullable String otherString; } static class TestConfOverrideWithUnderscoresComponent { @Named("che.some.name") @Inject @Nullable String string; @Named("che.some.other.name_with_underscores") @Inject @Nullable String otherString; } static class TestConfAliasComponent { @Named("che.some.name") @Inject String string; @Named("new.some.name") @Inject String otherString; @Named("very.new.some.name") @Inject String otherOtherString; } private void writePropertiesFile(File parent, String name, Properties properties) throws IOException { File propertiesFile = new File(parent, name); try (Writer writer = Files.newWriter(propertiesFile, Charset.forName("UTF-8"))) { properties.store(writer, null); } } private void setSystemProperties(Properties properties) throws IOException { for (Map.Entry<Object, Object> entry : properties.entrySet()) { systemPropertiesHelper.property((String)entry.getKey(), (String)entry.getValue()); } } private void mockServletContext() { servletContext = mock(ServletContext.class); when(servletContext.getInitParameterNames()).thenReturn(emptyEnumeration()); when(servletContext.getAttribute(Injector.class.getName())).thenAnswer( invocation -> retrieveComponentFromServletContext(Injector.class)); } private <T> T retrieveComponentFromServletContext(Class<T> componentType) { ArgumentCaptor<T> argumentCaptor = ArgumentCaptor.forClass(componentType); verify(servletContext, atLeastOnce()).setAttribute(eq(componentType.getName()), argumentCaptor.capture()); return argumentCaptor.getValue(); } }