Mercurial > hg > thermostat-ng > agent
changeset 2462:965485a1d071
Changes for basic windows (cygwin+mingw64) build
This build can run "thermostat setup" under Cygwin, but not much more.
Unit tests do not run and there is no provision for running without Cygwin
(No thermostat Windows *.cmd files have been written)
The environment is cygwin64 + mingw64 (installed via cygwin) and Windows 10.
line wrap: on
line diff
--- a/agent/core/Makefile Tue Sep 20 13:57:38 2016 -0400 +++ b/agent/core/Makefile Wed Sep 21 11:41:25 2016 -0400 @@ -1,24 +1,25 @@ CC = gcc JAVAH = javah -MYCFLAGS = -c -Wall -fPIC -MYLDFLAGS = -fPIC -shared +MYCFLAGS = -c -Wall -fPIC $(EXTRA_CFLAGS) +MYLDFLAGS = -fPIC -shared $(EXTRA_CFLAGS) COPY = cp -a CLASSPATH = target/classes/ TARGET_DIR = target +SO_PREFIX = lib +SO_SUFFIX = .so -INCLUDE = -I $(TARGET_DIR) -I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux +INCLUDE = -I $(TARGET_DIR) -I "$(JAVA_HOME)/include/" -I "$(JAVA_HOME)/include/$(JNI_PLATFORM)" + HOSTNAME_SOURCES = src/main/native/HostName.c HOSTNAME_TARGET = $(TARGET_DIR)/HostName.c HOSTNAME_OBJECTS = $(HOSTNAME_TARGET:.c=.o) - -HOSTNAME_EXECUTABLE = libHostNameWrapper.so +HOSTNAME_EXECUTABLE = $(SO_PREFIX)HostNameWrapper$(SO_SUFFIX) USERNAME_SOURCES = src/main/native/UserNameUtilImpl.c USERNAME_TARGET = $(TARGET_DIR)/UserNameUtilImpl.c USERNAME_OBJECTS = $(USERNAME_TARGET:.c=.o) - -USERNAME_EXECUTABLE = libUserNameUtilWrapper.so +USERNAME_EXECUTABLE = $(SO_PREFIX)UserNameUtilWrapper$(SO_SUFFIX) .PHONY: JNI_LIST = com.redhat.thermostat.agent.utils.hostname.HostName\ @@ -27,7 +28,8 @@ $(JNI_LIST): $(JAVAH) -force -classpath $(CLASSPATH) -d $(TARGET_DIR) $(JNI_LIST) -all: $(JNI_LIST) init $(HOSTNAME_SOURCES) $(HOSTNAME_EXECUTABLE) $(USERNAME_SOURCES) $(USERNAME_EXECUTABLE) +all: $(JNI_LIST) init $(HOSTNAME_SOURCES) $(HOSTNAME_EXECUTABLE) \ + $(USERNAME_SOURCES) $(USERNAME_EXECUTABLE) .PHONY: init: @@ -35,10 +37,10 @@ $(COPY) $(USERNAME_SOURCES) $(USERNAME_TARGET) $(HOSTNAME_EXECUTABLE): $(HOSTNAME_OBJECTS) - $(CC) $(HOSTNAME_OBJECTS) -o $(TARGET_DIR)/$@ $(MYLDFLAGS) $(LDFLAGS) + $(CC) $(HOSTNAME_OBJECTS) -o $(TARGET_DIR)/$@ $(MYLDFLAGS) $(LDFLAGS) $(PLATFORM_LIBS) $(USERNAME_EXECUTABLE): $(USERNAME_OBJECTS) - $(CC) $(USERNAME_OBJECTS) -o $(TARGET_DIR)/$@ $(MYLDFLAGS) $(LDFLAGS) + $(CC) $(USERNAME_OBJECTS) -o $(TARGET_DIR)/$@ $(MYLDFLAGS) $(LDFLAGS) $(PLATFORM_LIBS) .c.o: $(CC) $(MYCFLAGS) $(CFLAGS) $(INCLUDE) $< -o $@
--- a/agent/core/pom.xml Tue Sep 20 13:57:38 2016 -0400 +++ b/agent/core/pom.xml Wed Sep 21 11:41:25 2016 -0400 @@ -50,6 +50,37 @@ <name>Thermostat Agent Core</name> + <profiles> + <profile> + <id>default</id> + <activation> + <activeByDefault>true</activeByDefault> + </activation> + </profile> + + <profile> + <id>linux</id> + <activation> + <os><family>Unix</family></os> + </activation> + <properties> + <platform.libs></platform.libs> + </properties> + </profile> + + <profile> + <id>windows</id> + <activation> + <os><family>Windows</family></os> + </activation> + <properties> + <platform.libs>-lws2_32</platform.libs> + <sharedlib.prefix></sharedlib.prefix> + <sharedlib.suffix>.dll</sharedlib.suffix> + </properties> + </profile> + </profiles> + <dependencies> <dependency> <groupId>junit</groupId> @@ -159,6 +190,12 @@ <executable>make</executable> <arguments> <argument>all</argument> + <argument>CC=${c.compiler}</argument> + <argument>SO_PREFIX=${sharedlib.prefix}</argument> + <argument>SO_SUFFIX=${sharedlib.suffix}</argument> + <argument>EXTRA_CFLAGS=${cflags}</argument> + <argument>JNI_PLATFORM=${jni.platform}</argument> + <argument>PLATFORM_LIBS=${platform.libs}</argument> </arguments> <systemProperties> <systemProperty>
--- a/agent/core/src/main/native/HostName.c Tue Sep 20 13:57:38 2016 -0400 +++ b/agent/core/src/main/native/HostName.c Wed Sep 21 11:41:25 2016 -0400 @@ -40,7 +40,11 @@ #include <unistd.h> #include <string.h> -#include <netdb.h> +#if !defined(_WIN32) +# include <netdb.h> +#else // windows +# include <winsock2.h> +#endif #ifndef NI_MAXHOST #define NI_MAXHOST 1025
--- a/agent/core/src/main/native/UserNameUtilImpl.c Tue Sep 20 13:57:38 2016 -0400 +++ b/agent/core/src/main/native/UserNameUtilImpl.c Wed Sep 21 11:41:25 2016 -0400 @@ -37,12 +37,16 @@ #include "com_redhat_thermostat_utils_username_internal_UserNameUtilImpl.h" #include <jni.h> -#include <pwd.h> #include <string.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> +#if !defined(_WIN32) +# include <pwd.h> +#else // windows +#endif + static jint throw_IOException(JNIEnv *env, const char *message) { const char *class_name = "java/io/IOException"; jclass class = (*env)->FindClass(env, class_name); @@ -52,6 +56,8 @@ return (*env)->ThrowNew(env, class, message); } +#if !defined(_WIN32) + JNIEXPORT jstring JNICALL Java_com_redhat_thermostat_utils_username_internal_UserNameUtilImpl_getUserName0 (JNIEnv *env, jclass ProcessUserInfoBuilderClass, jlong uid) { @@ -91,3 +97,16 @@ return name; } +#else // Windows +JNIEXPORT jstring JNICALL +Java_com_redhat_thermostat_utils_username_internal_UserNameUtilImpl_getUserName0 + (JNIEnv *env, jclass ProcessUserInfoBuilderClass, jlong uid) { + + // TODO + throw_IOException(env, "UserNameUtilImpl.getUserName() no yet implemented on Windows"); + jstring name = (*env)->NewStringUTF(env, "xxxuserxxx"); + return name; +} +#endif + +
--- a/annotations/src/main/java/com/redhat/thermostat/annotations/internal/AnnotationProcessor.java Tue Sep 20 13:57:38 2016 -0400 +++ b/annotations/src/main/java/com/redhat/thermostat/annotations/internal/AnnotationProcessor.java Wed Sep 21 11:41:25 2016 -0400 @@ -109,7 +109,7 @@ try { FileObject filer = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", - "META-INF" + File.separator + "thermostat" + File.separator + "plugin-docs.xml", + "META-INF" + "/" + "thermostat" + "/" + "plugin-docs.xml", sourceElements); try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(filer.openOutputStream(), "UTF-8"))) { writeXml(writer, points);
--- a/common/core/src/main/java/com/redhat/thermostat/common/internal/test/TestCommandContextFactory.java Tue Sep 20 13:57:38 2016 -0400 +++ b/common/core/src/main/java/com/redhat/thermostat/common/internal/test/TestCommandContextFactory.java Wed Sep 21 11:41:25 2016 -0400 @@ -134,7 +134,8 @@ } catch (IOException e) { // ignore } - return new String(out.toByteArray(), consoleEncoding); + // get rid of <CR> in case we're on Windows + return new String(out.toByteArray(), consoleEncoding).replace("\r",""); } /** @@ -152,8 +153,9 @@ } } + // get rid of <CR> in case we're on Windows public String getError() { - return new String(err.toByteArray(), consoleEncoding); + return new String(err.toByteArray(), consoleEncoding).replace("\r",""); } public void reset() {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/distribution/assembly/core-assembly-windows.xml Wed Sep 21 11:41:25 2016 -0400 @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright 2012-2016 Red Hat, Inc. + + This file is part of Thermostat. + + Thermostat is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + Thermostat 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Thermostat; see the file COPYING. If not see + <http://www.gnu.org/licenses/>. + + Linking this code with other modules is making a combined work + based on this code. Thus, the terms and conditions of the GNU + General Public License cover the whole combination. + + As a special exception, the copyright holders of this code give + you permission to link this code with independent modules to + produce an executable, regardless of the license terms of these + independent modules, and to copy and distribute the resulting + executable under terms of your choice, provided that you also + meet, for each linked independent module, the terms and conditions + of the license of that module. An independent module is a module + which is not derived from or based on this code. If you modify + this code, you may extend this exception to your version of the + library, but you are not obligated to do so. If you do not wish + to do so, delete this exception statement from your version. + +--> +<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> + <id>core-assembly</id> + <formats> + <format>dir</format> + </formats> + <includeBaseDirectory>false</includeBaseDirectory> + + <dependencySets> + <dependencySet> + <useProjectArtifact>true</useProjectArtifact> + <unpack>false</unpack> + <includes> + <include>com.redhat.thermostat:thermostat-main</include> + <include>com.redhat.thermostat:thermostat-launcher</include> + <include>com.redhat.thermostat:thermostat-client-swing</include> + <include>com.redhat.thermostat:thermostat-swing-components</include> + <include>com.redhat.thermostat:thermostat-client-command</include> + <include>com.redhat.thermostat:thermostat-client-cli</include> + <include>com.redhat.thermostat:thermostat-osgi-living-vm-filter-swing</include> + <include>com.redhat.thermostat:thermostat-agent-core</include> + <include>com.redhat.thermostat:thermostat-agent-cli</include> + <include>com.redhat.thermostat:thermostat-agent-command</include> + <include>com.redhat.thermostat:thermostat-agent-command-server</include> + <include>com.redhat.thermostat:thermostat-agent-proxy-server</include> + <include>com.redhat.thermostat:thermostat-agent-ipc-unixsocket-server</include> + <include>com.redhat.thermostat:thermostat-agent-ipc-unixsocket-client</include> + <!--include>com.redhat.thermostat:thermostat-agent-ipc-tcpsocket-server</include> + <include>com.redhat.thermostat:thermostat-agent-ipc-tcpsocket-client</include--> + <include>com.redhat.thermostat:thermostat-common-core</include> + <include>com.redhat.thermostat:thermostat-common-command</include> + <include>com.redhat.thermostat:thermostat-osgi-process-handler</include> + <include>com.redhat.thermostat:thermostat-storage-cli</include> + <include>com.redhat.thermostat:thermostat-storage-mongodb</include> + <!--include>com.redhat.thermostat:thermostat-keyring</include--> + <include>com.redhat.thermostat:thermostat-web-client</include> + <include>com.redhat.thermostat:thermostat-system-backend</include> + <include>com.redhat.thermostat:thermostat-laf-utils</include> + <include>org.osgi:org.osgi.compendium</include> + <include>org.apache:org.apache.felix.scr</include> + <!-- Remove once upstream has OSGi metadata --> + <include>com.redhat.thermostat:jnr-x86asm</include> + </includes> + <excludes> + <exclude>org.osgi:org.osgi.core</exclude> + <!-- Exclude upstream jnr-x86asm jar in favour of our wrapped bundle --> + <exclude>com.github.jnr:jnr-x86asm</exclude> + </excludes> + <useTransitiveDependencies>true</useTransitiveDependencies> + <useTransitiveFiltering>true</useTransitiveFiltering> + </dependencySet> + </dependencySets> +</assembly> +
--- a/distribution/config/thermostatrc Tue Sep 20 13:57:38 2016 -0400 +++ b/distribution/config/thermostatrc Wed Sep 21 11:41:25 2016 -0400 @@ -48,11 +48,24 @@ # #JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk +if [ "$(expr substr $(uname -s) 1 6)" == "CYGWIN" ]; then + ##echo "Running under Cygwin" + export CYGWIN_MODE=1 +else + ##echo "Running under Linux" + export CYGWIN_MODE=0 +fi + # # Extra jar files which need to be on the classpath when # Thermostat boots. -# -THERMOSTAT_EXT_BOOT_CLASSPATH="${JAVA_HOME}/lib/tools.jar" +# +if [ $CYGWIN_MODE -eq 1 ]; then + THERMOSTAT_EXT_BOOT_CLASSPATH="`cygpath -u ${JAVA_HOME}/lib/tools.jar`" +else + THERMOSTAT_EXT_BOOT_CLASSPATH="${JAVA_HOME}/lib/tools.jar" +fi + # FIXME: Remove once jfreechart is a real OSGi bundle upstream THERMOSTAT_EXT_BOOT_CLASSPATH="${THERMOSTAT_EXT_BOOT_CLASSPATH}:${THERMOSTAT_HOME}/libs/jfreechart-@jfreechart.version@.jar" THERMOSTAT_EXT_BOOT_CLASSPATH="${THERMOSTAT_EXT_BOOT_CLASSPATH}:${THERMOSTAT_HOME}/libs/jcommon-@jcommon.version@.jar"
--- a/distribution/pom.xml Tue Sep 20 13:57:38 2016 -0400 +++ b/distribution/pom.xml Wed Sep 21 11:41:25 2016 -0400 @@ -59,6 +59,132 @@ <main.basedir>${project.basedir}/..</main.basedir> </properties> + <profiles> + <profile> + <id>default</id> + <activation> + <activeByDefault>true</activeByDefault> + </activation> + <properties> + <assemblyfile.suffix></assemblyfile.suffix> + </properties> + </profile> + + <profile> + <id>linux</id> + <activation> + <os><family>Unix</family></os> + </activation> + <properties> + <assemblyfile.suffix></assemblyfile.suffix> + </properties> + <dependencies> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-keyring</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-antrun-plugin</artifactId> + <executions> + <!--linux only files --> + <execution> + <id>directory-structure-linux</id> + <phase>prepare-package</phase> + <configuration> + <target> + <copy file="${main.basedir}/keyring/target/libGnomeKeyringWrapper.so" + todir="${project.build.directory}/image/libs/native" /> + <copy file="${main.basedir}/agent/core/target/libHostNameWrapper.so" + todir="${project.build.directory}/image/libs/native" /> + <copy file="${main.basedir}/agent/core/target/libUserNameUtilWrapper.so" + todir="${project.build.directory}/image/libs/native" /> + <copy file="${main.basedir}/laf-utils/target/libGTKThemeUtils.so" + todir="${project.build.directory}/image/libs/native" /> + </target> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <executions> + <execution> + <phase>integration-test</phase> + <goals> + <goal>exec</goal> + </goals> + </execution> + </executions> + <configuration> + <executable>${project.basedir}/tools/verify-bash-completion.sh</executable> + </configuration> + </plugin> + </plugins> + </build> + </profile> + + <profile> + <id>windows</id> + <activation> + <os><family>Windows</family></os> + </activation> + <properties> + <assemblyfile.suffix>-windows</assemblyfile.suffix> + </properties> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-antrun-plugin</artifactId> + <executions> + <!-- windows-only files--> + <execution> + <id>directory-structure-windows</id> + <phase>prepare-package</phase> + <configuration> + <target> + <!-- copy and rename the native libraries --> + <copy file="${main.basedir}/agent/core/target/HostNameWrapper.dll" + todir="${project.build.directory}/image/libs/native" /> + <copy file="${main.basedir}/agent/core/target/UserNameUtilWrapper.dll" + todir="${project.build.directory}/image/libs/native" /> + </target> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <executions> + <execution> + <phase>integration-test</phase> + <goals> + <goal>exec</goal> + </goals> + </execution> + </executions> + <configuration> + <executable>${project.basedir}/tools/verify-bash-completion.cmd</executable> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + <build> <plugins> <!-- skip unit test run--> @@ -106,7 +232,7 @@ <id>assemble-core</id> <configuration> <descriptors> - <descriptor>assembly/core-assembly.xml</descriptor> + <descriptor>assembly/core-assembly${assemblyfile.suffix}.xml</descriptor> </descriptors> <finalName>image/libs</finalName> <attach>false</attach> @@ -286,19 +412,10 @@ </goals> </execution> <execution> - <id>directory-structure</id> + <id>directory-structure-common</id> <phase>prepare-package</phase> <configuration> <target> - <!-- copy the native libraries --> - <copy file="${main.basedir}/keyring/target/libGnomeKeyringWrapper.so" - todir="${project.build.directory}/image/libs/native" /> - <copy file="${main.basedir}/agent/core/target/libHostNameWrapper.so" - todir="${project.build.directory}/image/libs/native" /> - <copy file="${main.basedir}/agent/core/target/libUserNameUtilWrapper.so" - todir="${project.build.directory}/image/libs/native" /> - <copy file="${main.basedir}/laf-utils/target/libGTKThemeUtils.so" - todir="${project.build.directory}/image/libs/native" /> <copy file="${main.basedir}/platform/swing/core/target/generated-sources/annotations/template.json" todir="${project.build.directory}/image/etc/plugins.d/platform" /> <copy file="${main.basedir}/thermostat-gui/core/target/generated-sources/annotations/gui.json" @@ -311,22 +428,6 @@ </execution> </executions> </plugin> - <!-- test bash completions --> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>exec-maven-plugin</artifactId> - <executions> - <execution> - <phase>integration-test</phase> - <goals> - <goal>exec</goal> - </goals> - </execution> - </executions> - <configuration> - <executable>${project.basedir}/tools/verify-bash-completion.sh</executable> - </configuration> - </plugin> </plugins> </build> @@ -440,11 +541,6 @@ </dependency> <dependency> <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-keyring</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.redhat.thermostat</groupId> <artifactId>thermostat-laf-utils</artifactId> <version>${project.version}</version> </dependency>
--- a/distribution/scripts/thermostat Tue Sep 20 13:57:38 2016 -0400 +++ b/distribution/scripts/thermostat Wed Sep 21 11:41:25 2016 -0400 @@ -54,7 +54,7 @@ BOOT_CLASSPATH="${BOOT_CLASSPATH}:${THERMOSTAT_LIBS}/thermostat-shared-config-@project.version@.jar" # Append extra class path entries coming from the profiles -if [ ! -z ${THERMOSTAT_EXT_BOOT_CLASSPATH} ]; then +if [ ! -z "${THERMOSTAT_EXT_BOOT_CLASSPATH}" ]; then BOOT_CLASSPATH="${BOOT_CLASSPATH}:${THERMOSTAT_EXT_BOOT_CLASSPATH}" fi @@ -96,22 +96,38 @@ esac done +# if running on cygwin, modify THERMOSTAT_HOME and BOOT_CLASSPATH to windows for the JDK +if [ $CYGWIN_MODE -eq 1 ]; then + export THERMOSTAT_HOME="`cygpath -w $THERMOSTAT_HOME`" + export USER_THERMOSTAT_HOME="`cygpath -w $USER_THERMOSTAT_HOME`" + BOOT_CLASSPATH=$(cygpath -w -p "$BOOT_CLASSPATH") + ##env + # in a VM, jline can cause 100% CPU usage on Windows without this + THERMOSTAT_EXT_JAVA_OPTS="$THERMOSTAT_EXT_JAVA_OPTS -Djline.terminal=jline.UnsupportedTerminal" + # in cygwin, the openJDK uses c:\home\user as java.home. which is unhelpful. + # pass the cygwin home directory instead + THERMOSTAT_EXT_JAVA_OPTS="$THERMOSTAT_EXT_JAVA_OPTS -Duser.home=`cygpath -w $HOME`" +else + export THERMOSTAT_HOME + export USER_THERMOSTAT_HOME +fi + # Finally run thermostat (optionally in the background) if [ ${RUN_IN_BG} -eq 1 ]; then # The thermostat-agent-sysd script uses this. if [ x"${PID_FILE}" = "x" ]; then usage else - ${JAVA} ${THERMOSTAT_EXT_JAVA_OPTS} ${LOGGING_ARGS} "${JAVA_ARGS[@]}" \ - -cp ${BOOT_CLASSPATH} \ + "${JAVA}" ${THERMOSTAT_EXT_JAVA_OPTS} ${LOGGING_ARGS} "${JAVA_ARGS[@]}" \ + -cp "${BOOT_CLASSPATH}" \ ${THERMOSTAT_MAIN} ${THERMOSTAT_EXT_OPTS} "${ARGS[@]}" & retval=$? echo $! > ${PID_FILE} retval=$(( ${retval} + $? )) fi else - ${JAVA} ${THERMOSTAT_EXT_JAVA_OPTS} ${LOGGING_ARGS} "${JAVA_ARGS[@]}" \ - -cp ${BOOT_CLASSPATH} \ + "${JAVA}" ${THERMOSTAT_EXT_JAVA_OPTS} ${LOGGING_ARGS} "${JAVA_ARGS[@]}" \ + -cp "${BOOT_CLASSPATH}" \ ${THERMOSTAT_MAIN} ${THERMOSTAT_EXT_OPTS} "${ARGS[@]}" retval=$? fi
--- a/distribution/scripts/thermostat-agent-proxy Tue Sep 20 13:57:38 2016 -0400 +++ b/distribution/scripts/thermostat-agent-proxy Wed Sep 21 11:41:25 2016 -0400 @@ -72,10 +72,20 @@ fi # Start server -CONFIG_FILE_ARG="-DipcConfigFile=${CONFIG_FILE}" -# Drop permissions, if root -if [ "$(id -u)" -eq 0 ]; then - /bin/su -s /bin/bash -c "${JAVA} -cp ${IPC_CLASSPATH} ${CONFIG_FILE_ARG} ${LOGGING_ARGS} ${DEBUG_OPTS} ${AGENT_PROXY_CLASS} ${TARGET_PID}" "${TARGET_USER}" +if [ $CYGWIN_MODE -eq 1 ]; then + CONFIG_FILE_ARG="-DipcConfigFile=`cygpath -w ${CONFIG_FILE}`" + # Drop permissions, if root + if [ "$(id -u)" -eq 0 ]; then + /bin/su -s /bin/bash -c "${JAVA} -cp `cygpath -w -p ${IPC_CLASSPATH}` ${CONFIG_FILE_ARG} ${LOGGING_ARGS} ${DEBUG_OPTS} ${AGENT_PROXY_CLASS} ${TARGET_PID}" "${TARGET_USER}" + else + ${JAVA} -cp `cygpath -w -p ${IPC_CLASSPATH}` "${CONFIG_FILE_ARG}" ${DEBUG_OPTS} ${LOGGING_ARGS} ${AGENT_PROXY_CLASS} "${TARGET_PID}" + fi else - ${JAVA} -cp ${IPC_CLASSPATH} "${CONFIG_FILE_ARG}" ${DEBUG_OPTS} ${LOGGING_ARGS} ${AGENT_PROXY_CLASS} "${TARGET_PID}" + CONFIG_FILE_ARG="-DipcConfigFile=${CONFIG_FILE}" + # Drop permissions, if root + if [ "$(id -u)" -eq 0 ]; then + /bin/su -s /bin/bash -c "${JAVA} -cp ${IPC_CLASSPATH} ${CONFIG_FILE_ARG} ${LOGGING_ARGS} ${DEBUG_OPTS} ${AGENT_PROXY_CLASS} ${TARGET_PID}" "${TARGET_USER}" + else + ${JAVA} -cp ${IPC_CLASSPATH} "${CONFIG_FILE_ARG}" ${DEBUG_OPTS} ${LOGGING_ARGS} ${AGENT_PROXY_CLASS} "${TARGET_PID}" + fi fi
--- a/distribution/scripts/thermostat-command-channel Tue Sep 20 13:57:38 2016 -0400 +++ b/distribution/scripts/thermostat-command-channel Wed Sep 21 11:41:25 2016 -0400 @@ -81,10 +81,20 @@ SCRIPT_OWNER=$(stat -c '%U' "${THIS_SCRIPT}") # Start server -CONFIG_FILE_ARG="-DipcConfigFile=${CONFIG_FILE}" -# Drop permissions, if root -if [ "$(id -u)" -eq 0 ]; then - exec /bin/su -s /bin/bash -c "${JAVA} ${CONFIG_FILE_ARG} ${LOGGING_ARGS} -cp ${IPC_CLASSPATH} ${DEBUG_OPTS} ${CMD_CHANNEL_CLASS} ${HOSTNAME} ${PORT}" "${SCRIPT_OWNER}" +if [ $CYGWIN_MODE -eq 1 ]; then + CONFIG_FILE_ARG="-DipcConfigFile=`cygpath -w ${CONFIG_FILE}`" + # Drop permissions, if root + if [ "$(id -u)" -eq 0 ]; then + exec /bin/su -s /bin/bash -c "${JAVA} ${CONFIG_FILE_ARG} ${LOGGING_ARGS} -cp `cygpath -w -p ${IPC_CLASSPATH}` ${DEBUG_OPTS} ${CMD_CHANNEL_CLASS} ${HOSTNAME} ${PORT}" "${SCRIPT_OWNER}" + else + exec ${JAVA} "${CONFIG_FILE_ARG}" ${LOGGING_ARGS} -cp "`cygpath -w -p ${IPC_CLASSPATH}`" ${DEBUG_OPTS} "${CMD_CHANNEL_CLASS}" "${HOSTNAME}" "${PORT}" + fi else - exec ${JAVA} "${CONFIG_FILE_ARG}" ${LOGGING_ARGS} -cp "${IPC_CLASSPATH}" ${DEBUG_OPTS} "${CMD_CHANNEL_CLASS}" "${HOSTNAME}" "${PORT}" + CONFIG_FILE_ARG="-DipcConfigFile=${CONFIG_FILE}" + # Drop permissions, if root + if [ "$(id -u)" -eq 0 ]; then + exec /bin/su -s /bin/bash -c "${JAVA} ${CONFIG_FILE_ARG} ${LOGGING_ARGS} -cp ${IPC_CLASSPATH} ${DEBUG_OPTS} ${CMD_CHANNEL_CLASS} ${HOSTNAME} ${PORT}" "${SCRIPT_OWNER}" + else + exec ${JAVA} "${CONFIG_FILE_ARG}" ${LOGGING_ARGS} -cp ${IPC_CLASSPATH} ${DEBUG_OPTS} "${CMD_CHANNEL_CLASS}" "${HOSTNAME}" "${PORT}" + fi fi
--- a/distribution/scripts/thermostat-common Tue Sep 20 13:57:38 2016 -0400 +++ b/distribution/scripts/thermostat-common Wed Sep 21 11:41:25 2016 -0400 @@ -50,11 +50,29 @@ echo "$DIR" } +# set global variable for Cygwin testing +# between all shell files, we pass cygwin-compatible paths, +# and let each script decide when to convert them. +# an exception is command line args for java programs, which +# need to be converted to windows format at creation time +if [ "$(expr substr $(uname -s) 1 6)" == "CYGWIN" ]; then + ##echo "Running under Cygwin" + export CYGWIN_MODE=1 +else + ##echo "Running under Linux" + export CYGWIN_MODE=0 +fi + # Thermostat home if [[ "${THERMOSTAT_HOME}" = "" ]]; then THERMOSTAT_HOME="$(_find_thermostat_home)" fi -export THERMOSTAT_HOME + +# on cygwin, convert to Unix format +if [ $CYGWIN_MODE -eq 1 ]; then + THERMOSTAT_HOME="`cygpath -u $THERMOSTAT_HOME`" + export THERMOSTAT_HOME +fi # Thermostat user home if [[ "${USER_THERMOSTAT_HOME}" = "" ]]; then @@ -69,7 +87,11 @@ THERMOSTAT_MAIN="com.redhat.thermostat.main.Thermostat" if [[ "${JAVA_HOME}" = "" ]]; then - jdk_home_candidate="@thermostat.jdk.home@" + if [[ "${JDK_HOME}" != "" ]]; then + jdk_home_candidate="${JDK_HOME}" + else + jdk_home_candidate="@thermostat.jdk.home@" + fi if [[ -e "${jdk_home_candidate}/bin/javac" ]]; then JAVA_HOME="${jdk_home_candidate}" else @@ -96,14 +118,23 @@ JAVA="${JAVA_HOME}/bin/java" +# on Cygwin, pass the windows path in all Java options SYSTEM_LOG_CONFIG_FILE="${THERMOSTAT_HOME}/etc/logging.properties" if [ -f "${SYSTEM_LOG_CONFIG_FILE}" ] ; then - LOGGING_ARGS="-Djava.util.logging.config.file=${SYSTEM_LOG_CONFIG_FILE}" + if [ $CYGWIN_MODE -eq 1 ]; then + LOGGING_ARGS="-Djava.util.logging.config.file=`cygpath -w ${SYSTEM_LOG_CONFIG_FILE}`" + else + LOGGING_ARGS="-Djava.util.logging.config.file=${SYSTEM_LOG_CONFIG_FILE}" + fi fi USER_LOG_CONFIG_FILE="${USER_THERMOSTAT_HOME}/etc/logging.properties" if [ -f "${USER_LOG_CONFIG_FILE}" ] ; then - LOGGING_ARGS="-Djava.util.logging.config.file=${USER_LOG_CONFIG_FILE}" + if [ $CYGWIN_MODE -eq 1 ]; then + LOGGING_ARGS="-Djava.util.logging.config.file=`cygpath -w ${USER_LOG_CONFIG_FILE}`" + else + LOGGING_ARGS="-Djava.util.logging.config.file=${USER_LOG_CONFIG_FILE}" + fi fi LOGGING_ARGS="${LOGGING_ARGS} -Djline.log.jul=true"
--- a/distribution/scripts/thermostat-webservice Tue Sep 20 13:57:38 2016 -0400 +++ b/distribution/scripts/thermostat-webservice Wed Sep 21 11:41:25 2016 -0400 @@ -171,7 +171,11 @@ do_start() { rm -rf webapps/thermostat cp -r "$TH/web/war/target/thermostat-web-war-@project.version@" webapps/thermostat - JAVA_OPTS="-Djava.security.auth.login.config=${TH}/distribution/target/image/etc/thermostat_jaas.conf" ./bin/startup.sh + if [ $CYGWIN_MODE -eq 1 ]; then + JAVA_OPTS="-Djava.security.auth.login.config=`cygpath -w ${TH}/distribution/target/image/etc/thermostat_jaas.conf`" ./bin/startup.sh + else + JAVA_OPTS="-Djava.security.auth.login.config=${TH}/distribution/target/image/etc/thermostat_jaas.conf" ./bin/startup.sh + fi } do_stop() {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/distribution/tools/verify-bash-completion.cmd Wed Sep 21 11:41:25 2016 -0400 @@ -0,0 +1,12 @@ +@echo off + +set CMD_DIR=%~dp0 +set CYGWIN_DIR=c:\cygwin64 +set PATH=%PATH%;%CYGWIN_DIR%\bin + +for /f "delims=" %%a in ('%CYGWIN_DIR%\bin\cygpath -u %CMD_DIR%') do @set CMD_DIR=%%a + +%CYGWIN_DIR%\bin\bash -c %CMD_DIR%verify-bash-completion.sh + +rem echo XXX ERRR = %errorlevel% +exit /b %errorlevel%
--- a/distribution/tools/verify-bash-completion.sh Tue Sep 20 13:57:38 2016 -0400 +++ b/distribution/tools/verify-bash-completion.sh Wed Sep 21 11:41:25 2016 -0400 @@ -98,6 +98,8 @@ input=$1 expected=$2 expected_pretty=$(echo $expected | __prettify) + # clean up (cygwin doesn't seem to delete redirect files if no output) + rm -f ${TARGET}/completion.actual # save completions and any other output separately and check both __find_completion $input >${TARGET}/completion.actual 2>${TARGET}/completion.output actual=$(<${TARGET}/completion.actual)
--- a/laf-utils/Makefile Tue Sep 20 13:57:38 2016 -0400 +++ b/laf-utils/Makefile Wed Sep 21 11:41:25 2016 -0400 @@ -1,18 +1,20 @@ CC = gcc JAVAH = javah -MYCFLAGS = -c -Wall -fPIC -MYLDFLAGS = -fPIC -shared +MYCFLAGS = -c -Wall -fPIC $(EXTRA_CFLAGS) +MYLDFLAGS = -fPIC -shared $(EXTRA_CFLAGS) COPY = cp -a CLASSPATH = target/classes/ TARGET_DIR = target +SO_PREFIX = lib +SO_SUFFIX = .so -INCLUDE = -I $(TARGET_DIR) -I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux +INCLUDE = -I $(TARGET_DIR) -I "$(JAVA_HOME)/include/" -I "$(JAVA_HOME)/include/$(JNI_PLATFORM)" SOURCES = src/main/native/GTKThemeUtils.c TARGET = $(TARGET_DIR)/GTKThemeUtils.c OBJECTS = $(TARGET:.c=.o) -EXECUTABLE = libGTKThemeUtils.so +EXECUTABLE = $(SO_PREFIX)GTKThemeUtils$(SO_SUFFIX) MYCFLAGS += `pkg-config --cflags gtk+-2.0` MYCFLAGS += `pkg-config gthread-2.0 --cflags`
--- a/laf-utils/pom.xml Tue Sep 20 13:57:38 2016 -0400 +++ b/laf-utils/pom.xml Wed Sep 21 11:41:25 2016 -0400 @@ -48,6 +48,51 @@ <packaging>bundle</packaging> <name>Thermostat Look And Feel Utils</name> + <!-- only build the native code on Linux et. al. --> + <profiles> + <profile> + <id>linux</id> + <activation> + <os><family>unix</family></os> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <executions> + <execution> + <phase>compile</phase> + <goals> + <goal>exec</goal> + </goals> + </execution> + </executions> + <configuration> + <executable>make</executable> + <arguments> + <argument>all</argument> + <argument>CC=${c.compiler}</argument> + <argument>SO_PREFIX=${sharedlib.prefix}</argument> + <argument>SO_SUFFIX=${sharedlib.suffix}</argument> + <argument>EXTRA_CFLAGS=${cflags}</argument> + <argument>JNI_PLATFORM=${jni.platform}</argument> + <argument>PLATFORM_LIBS=${platform.libs}</argument> + </arguments> + <systemProperties> + <systemProperty> + <key>JAVA_HOME</key> + <value>${java.home}</value> + </systemProperty> + </systemProperties> + </configuration> + </plugin> + </plugins> + </build> + </profile> + + </profiles> + <build> <plugins> <plugin> @@ -69,31 +114,6 @@ </instructions> </configuration> </plugin> - - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>exec-maven-plugin</artifactId> - <executions> - <execution> - <phase>compile</phase> - <goals> - <goal>exec</goal> - </goals> - </execution> - </executions> - <configuration> - <executable>make</executable> - <arguments> - <argument>all</argument> - </arguments> - <systemProperties> - <systemProperty> - <key>JAVA_HOME</key> - <value>${java.home}</value> - </systemProperty> - </systemProperties> - </configuration> - </plugin> </plugins>
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java Tue Sep 20 13:57:38 2016 -0400 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java Wed Sep 21 11:41:25 2016 -0400 @@ -440,7 +440,7 @@ private String parseDescription(Node descriptionNode) { String text = descriptionNode.getTextContent().trim(); - String[] lines = text.split("(" + System.lineSeparator() + ")+"); + String[] lines = text.split("(\r\n|\r|\n)+"); StringBuilder result = new StringBuilder(); for (String line : lines) { result.append(line.trim());
--- a/pom.xml Tue Sep 20 13:57:38 2016 -0400 +++ b/pom.xml Wed Sep 21 11:41:25 2016 -0400 @@ -57,6 +57,42 @@ <license.skip>false</license.skip> </properties> </profile> + + <profile> + <id>linux</id> + <activation> + <os><family>Unix</family></os> + </activation> + <properties> + <script.extension>.sh</script.extension> + <c.compiler>gcc</c.compiler> + <cflags></cflags> + <jni.platform>linux</jni.platform> + <sharedlib.prefix>lib</sharedlib.prefix> + <sharedlib.suffix>.so</sharedlib.suffix> + </properties> + <modules> + <module>keyring</module> + </modules> + </profile> + + <profile> + <id>windows</id> + <activation> + <os><family>Windows</family></os> + </activation> + <properties> + <script.extension>.cmd</script.extension> + <c.compiler>x86_64-w64-mingw32-gcc -std=c99</c.compiler> + <cflags>-std=c99</cflags> + <cygwin.dir>c:/cygwin64</cygwin.dir> + <jni.platform>win32</jni.platform> + <sharedlib.prefix></sharedlib.prefix> + <sharedlib.suffix>.dll</sharedlib.suffix> + </properties> + </profile> + + <profile> <!-- Some thermostat code uses sun.jvmstat and com.sun.tools.attach. These are only available via tools.jar prior to JDK 9 --> @@ -312,7 +348,6 @@ <module>agent</module> <module>client</module> <module>unix-process-handler</module> - <module>keyring</module> <module>thread</module> <module>killvm</module> <module>web</module>
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/CredentialsFileCreator.java Tue Sep 20 13:57:38 2016 -0400 +++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/CredentialsFileCreator.java Wed Sep 21 11:41:25 2016 -0400 @@ -45,7 +45,9 @@ import java.util.Set; class CredentialsFileCreator { - + + private static final boolean IS_UNIX = !System.getProperty("os.name").contains("Windows"); + private static final Set<PosixFilePermission> CREDS_FILE_PERMISSIONS = EnumSet.of( PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE @@ -53,8 +55,15 @@ void create(File file) throws IOException { if (!file.exists()) { - //create file and set file permissions to 600 - Files.createFile(file.toPath(), PosixFilePermissions.asFileAttribute(CREDS_FILE_PERMISSIONS)); + // create file and set file permissions to 600 + if ( IS_UNIX ) + Files.createFile(file.toPath(), PosixFilePermissions.asFileAttribute(CREDS_FILE_PERMISSIONS)); + else { + // on windows, credentials may be globally visible. + // PosixFilePermissions don't work on windows (throws exception even!) + // the code below doesn't enforce this (but should) + final boolean success = file.createNewFile() && file.setReadable(true, true) && file.setWritable(true, true); + } } } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/MongoOSUtilFactory.java Wed Sep 21 11:41:25 2016 -0400 @@ -0,0 +1,53 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.storage.cli.internal; + +/** + * factory class to create OS-specific implementations of MongoOSUtil + */ +class MongoOSUtilFactory { + + private static final boolean IS_UNIX = !System.getProperty("os.name").contains("Windows"); + + private static final MongoOSUtilFactory theInstance = new MongoOSUtilFactory(); + + static MongoOSUtilFactory instance() { return theInstance; } + + MongoOSUtilInterface createMongoOSUtil() { + return IS_UNIX ? new MongoUnixUtil() : new MongoWindowsUtil(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/MongoOSUtilInterface.java Wed Sep 21 11:41:25 2016 -0400 @@ -0,0 +1,45 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.storage.cli.internal; + +/** + * abstract out all the OS-specific chunks from MongoProcessRunner + */ +interface MongoOSUtilInterface { + String[] getMongoStartCmd(); + String[] getMongoStopCmd(); +}
--- a/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/MongoProcessRunner.java Tue Sep 20 13:57:38 2016 -0400 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/MongoProcessRunner.java Wed Sep 21 11:41:25 2016 -0400 @@ -63,7 +63,7 @@ private static final boolean profile; private static final int profileLevel; - + static { profile = Boolean.getBoolean("thermostat.storage.mongo.profile"); profileLevel = Integer.getInteger("thermostat.storage.mongo.profile.slowms", 100); @@ -74,14 +74,6 @@ private static final String MONGO_PROCESS = "mongod"; - private static final String [] MONGO_BASIC_ARGS = { - "mongod", "--quiet", "--fork", "--auth", "--nohttpinterface", "--bind_ip" - }; - - private static final String [] MONGO_SHUTDOWN_ARGS = { - "kill", "-s", "TERM" - }; - private static final String EMPTY_STRING = ""; private static final String NO_JOURNAL_ARGUMENT = "--nojournal"; @@ -93,7 +85,9 @@ private Integer pid; private final boolean isQuiet; private final boolean permitLocalhostExpn; - + + private final static MongoOSUtilInterface util = MongoOSUtilFactory.instance().createMongoOSUtil(); + public MongoProcessRunner(UNIXProcessHandler processHandler, DBStartupConfiguration configuration, boolean quiet, boolean permitLocalhostException) { this.processHandler = processHandler; this.configuration = configuration; @@ -226,7 +220,7 @@ LocalizedString message = translator.localize(LocaleResources.STORAGE_NOT_RUNNING); throw new StorageNotRunningException(message.getContents()); } - List<String> commands = new ArrayList<>(Arrays.asList(MONGO_SHUTDOWN_ARGS)); + List<String> commands = new ArrayList<>(Arrays.asList(util.getMongoStopCmd())); commands.add(String.valueOf(pid)); LoggedExternalProcess process = new LoggedExternalProcess(commands); @@ -263,12 +257,12 @@ } List<String> getStartupCommand(String dbVersion) throws IOException, InvalidConfigurationException { - List<String> commands = new ArrayList<>(Arrays.asList(MONGO_BASIC_ARGS)); + List<String> commands = new ArrayList<>(Arrays.asList(util.getMongoStartCmd())); commands.add(configuration.getBindIP()); if (dbVersion.compareTo(NO_JOURNAL_FIRST_VERSION) >= 0) { - commands.add(1, NO_JOURNAL_ARGUMENT); + commands.add(NO_JOURNAL_ARGUMENT); } commands.add("--dbpath");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/MongoUnixUtil.java Wed Sep 21 11:41:25 2016 -0400 @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.storage.cli.internal; + +/** + * unix-specific code for Mondo runner + */ +class MongoUnixUtil implements MongoOSUtilInterface { + + private static final String[] UNIX_MONGO_START_CMD = { + "mongod", "--quiet", "--fork", "--auth", "--nohttpinterface", "--bind_ip" + }; + private static final String[] UNIX_MONGO_STOP_CMD = { + "kill", "-s", "TERM" + }; + + @Override + public String[] getMongoStartCmd() { + return UNIX_MONGO_START_CMD; + } + + @Override + public String[] getMongoStopCmd() { + return UNIX_MONGO_STOP_CMD; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/main/java/com/redhat/thermostat/storage/cli/internal/MongoWindowsUtil.java Wed Sep 21 11:41:25 2016 -0400 @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.storage.cli.internal; + +/** + * Windows-specific code for Mondo runner + */ +class MongoWindowsUtil implements MongoOSUtilInterface { + + private static final String[] WIN_MONGO_START_CMD = { + "cmd", "/c", "start", "/b", "mongod", "--quiet", "--auth", "--nohttpinterface", "--bind_ip" + }; + private static final String[] WIN_MONGO_STOP_CMD = { + "cmd", "/c", "taskkill", "/F", "/PID" + }; + + @Override + public String[] getMongoStartCmd() { + return WIN_MONGO_START_CMD; + } + + @Override + public String[] getMongoStopCmd() { + return WIN_MONGO_STOP_CMD; + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/MongoOSUtilFactoryTest.java Wed Sep 21 11:41:25 2016 -0400 @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.storage.cli.internal; + +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class MongoOSUtilFactoryTest { + + private static final boolean IS_UNIX = !System.getProperty("os.name").contains("Windows"); + + @Test + public void instanceIsntNullTest() { + assertNotNull(MongoOSUtilFactory.instance()); + } + + @Test + public void testCorrectOs() { + assertNotNull(MongoOSUtilFactory.instance()); + + MongoOSUtilInterface thing = MongoOSUtilFactory.instance().createMongoOSUtil(); + assertNotNull(thing); + + if (IS_UNIX) + assertTrue("createMongoOSUtil() must return an instance of MongoUnixUtil on unix", thing instanceof MongoUnixUtil); + else + assertTrue("createMongoOSUtil() must return an instance of MongoWindowsUtil on Windows", thing instanceof MongoWindowsUtil); + } +}
--- a/storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/MongoProcessRunnerTest.java Tue Sep 20 13:57:38 2016 -0400 +++ b/storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/MongoProcessRunnerTest.java Wed Sep 21 11:41:25 2016 -0400 @@ -111,9 +111,9 @@ @Test public void testCommandArgumentsWithLatestMongodbVersion() throws Exception { - String[] expected = { "mongod", "--nojournal", "--quiet", "--fork", + String[] expected = { "mongod", "--quiet", "--fork", "--auth", "--nohttpinterface", "--bind_ip", config.getBindIP(), - "--dbpath", config.getDBPath().getCanonicalPath(), "--logpath", + "--nojournal", "--dbpath", config.getDBPath().getCanonicalPath(), "--logpath", config.getLogFile().getCanonicalPath(), "--pidfilepath", config.getPidFile().getCanonicalPath(), "--port", Long.toString(config.getPort()), "--setParameter", "enableLocalhostAuthBypass=0"};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/MongoUnixUtilTest.java Wed Sep 21 11:41:25 2016 -0400 @@ -0,0 +1,70 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.storage.cli.internal; + +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +public class MongoUnixUtilTest { + + @Test + public void testGetStartCmd() { + String[] cmd = new MongoUnixUtil().getMongoStartCmd(); + assertNotNull(cmd); + testContains(cmd, "mongod"); + testContains(cmd, "--fork"); + } + + @Test + public void testGetStopCmd() { + String[] cmd = new MongoUnixUtil().getMongoStopCmd(); + assertNotNull(cmd); + testContains(cmd, "kill"); + testContains(cmd, "TERM"); + } + + private static void testContains(final String[] list, final String item) { + boolean hasItem = false; + for (final String a : list) { + if ( item.equals(a) ) + return; + } + fail("array doesn't contain '"+item+"'"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/MongoWindowsUtilTest.java Wed Sep 21 11:41:25 2016 -0400 @@ -0,0 +1,75 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.storage.cli.internal; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +public class MongoWindowsUtilTest { + + @Test + public void testGetStartCmd() { + String[] cmd = new MongoWindowsUtil().getMongoStartCmd(); + assertNotNull(cmd); + testContains(cmd, "cmd"); + testContains(cmd, "mongod"); + testContains(cmd, "start"); + } + + @Test + public void testGetStopCmd() { + String[] cmd = new MongoWindowsUtil().getMongoStopCmd(); + assertNotNull(cmd); + testContains(cmd, "cmd"); + testContains(cmd, "taskkill"); + testContains(cmd, "/pid"); + } + + private static void testContains(final String[] list, final String item) { + boolean hasItem = false; + for (final String a : list) { + if ( item.equalsIgnoreCase(a) ) + return; + } + fail("array doesn't contain '"+item+"'"); + } +}
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/config/FileStorageCredentials.java Tue Sep 20 13:57:38 2016 -0400 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/config/FileStorageCredentials.java Wed Sep 21 11:41:25 2016 -0400 @@ -57,8 +57,6 @@ private static final char[] user = {'u', 's', 'e', 'r', 'n', 'a', 'm', 'e'}; private static final char comment = '#'; - private final String newLine; - private final File authFile; private final Reader testingAuthReader; private final int authDataLength; @@ -70,7 +68,6 @@ } this.testingAuthReader = null; this.authFile = authFile; - newLine = System.lineSeparator(); long length = this.authFile.length(); if (length > Integer.MAX_VALUE || length < 0L) { // Unlikely issue with authFile, try to get path to share with user via exception message @@ -89,12 +86,11 @@ } // Testing constructor - FileStorageCredentials(Reader authReader, String lineSeparator) { + FileStorageCredentials(Reader authReader) { if (authReader == null) { throw new IllegalArgumentException("authReader must not be null"); } this.testingAuthReader = authReader; - newLine = lineSeparator; long length = -1; try { length = testingAuthReader.skip(Long.MAX_VALUE); @@ -110,11 +106,6 @@ initUsername(); } - // Testing constructor - FileStorageCredentials(Reader agentAuthReader) { - this(agentAuthReader, System.lineSeparator()); - } - @Override public String getUsername() { return username; @@ -252,20 +243,34 @@ private int nextLine(char[] data, int current) { int nextNewLine = getPositionOfNextNewline(data, current); - return nextNewLine + newLine.length(); + + int nlLength = 1; + if ((nextNewLine+1) < data.length) { + final char n0 = data[nextNewLine]; + final char n1 = data[nextNewLine+1]; + if (n0 == '\r' && n1 == '\n') { + nlLength = 2; + } + else if (n0 == '\n' && n1 == '\r') { + nlLength = 2; + } + } + return nextNewLine + nlLength; } + // a newline is defined as '\n', optionally preceded with a '\r' (for windows compatiblity) + // using System.lineSeparator() means a file editted on one platform may be unreadable on another. private int getPositionOfNextNewline(char[] data, int current) { assert( current <= data.length ); int next = current; while (next < data.length) { + final char c = data[next]; boolean newLineFound = false; - for (int i = 0; i < newLine.length(); i++) { - if (data[next + i] == newLine.charAt(i)) { - newLineFound = true; - } else { - newLineFound = false; - } + + if (c == '\n' || c == '\r') { + newLineFound = true; + } else { + newLineFound = false; } if (newLineFound) { break;
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/config/FileStorageCredentialsTest.java Tue Sep 20 13:57:38 2016 -0400 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/config/FileStorageCredentialsTest.java Wed Sep 21 11:41:25 2016 -0400 @@ -57,6 +57,33 @@ } @Test + public void testAuthConfigCanReadWindows() { + Reader reader = new StringReader("username=user\r\npassword=pass"); + FileStorageCredentials creds = new FileStorageCredentials(reader); + Assert.assertEquals("user", creds.getUsername()); + Assert.assertEquals(4, creds.getPassword().length); + Assert.assertEquals("pass", new String(creds.getPassword())); + } + + @Test + public void testAuthConfigCanReadReversed() { + Reader reader = new StringReader("username=user\n\rpassword=pass\n"); + FileStorageCredentials creds = new FileStorageCredentials(reader); + Assert.assertEquals("user", creds.getUsername()); + Assert.assertEquals(4, creds.getPassword().length); + Assert.assertEquals("pass", new String(creds.getPassword())); + } + + @Test + public void testAuthConfigCanReadOldschoolMac() { + Reader reader = new StringReader("username=user\r\rpassword=pass\r"); + FileStorageCredentials creds = new FileStorageCredentials(reader); + Assert.assertEquals("user", creds.getUsername()); + Assert.assertEquals(4, creds.getPassword().length); + Assert.assertEquals("pass", new String(creds.getPassword())); + } + + @Test public void testAuthConfig() { Reader reader = new StringReader("username=user\npassword=pass\n"); FileStorageCredentials creds = new FileStorageCredentials(reader); @@ -123,7 +150,7 @@ @Test public void testAlternateNewLine() { Reader reader = new StringReader("username=user\r\npassword=pass\r\n"); - FileStorageCredentials creds = new FileStorageCredentials(reader, "\r\n"); + FileStorageCredentials creds = new FileStorageCredentials(reader); Assert.assertEquals("user", creds.getUsername()); Assert.assertEquals("pass", new String(creds.getPassword())); }
--- a/unix-process-handler/src/main/java/com/redhat/thermostat/service/internal/UnixProcessUtilities.java Tue Sep 20 13:57:38 2016 -0400 +++ b/unix-process-handler/src/main/java/com/redhat/thermostat/service/internal/UnixProcessUtilities.java Wed Sep 21 11:41:25 2016 -0400 @@ -54,14 +54,39 @@ class UnixProcessUtilities implements UNIXProcessHandler { private static final Logger logger = LoggingUtils.getLogger(UnixProcessUtilities.class); - - private static final UNIXProcessHandler instance = new UnixProcessUtilities(); + + private static final boolean IS_UNIX = !System.getProperty("os.name").contains("Windows"); + + private static final UNIXProcessHandler instance = IS_UNIX ? new UnixProcessUtilities() : new WindowsProcessUtilities(); public static UNIXProcessHandler getInstance() { return instance; } UnixProcessUtilities() {} - + + static class WindowsProcessUtilities extends UnixProcessUtilities { + public WindowsProcessUtilities() {} + + List<String> buildCommandLine(Integer pid) { + final List<String> commandLine = new ArrayList<>(); + commandLine.add("tasklist"); + commandLine.add("/FO"); + commandLine.add("csv"); + commandLine.add("/FI"); + commandLine.add("\"PID eq " + String.valueOf(pid) + "\""); + return commandLine; + } + + String processStdout(final String outStr) { + final String [] output = outStr.split(","); + String result = output[0]; + if (result.length() >= 2 && result.charAt(0) == '"') + result = result.replace("\"",""); + result = result.replace(".exe",""); + return result; + } + } + @Override public void sendSignal(Integer pid, UNIXSignal signal) { exec("kill -s " + signal.signalName() + " " + pid); @@ -75,29 +100,38 @@ logger.log(Level.WARNING, "can't run kill!", e); } } - + + List<String> buildCommandLine(Integer pid) { + final List<String> commandLine = new ArrayList<>(); + commandLine.add("ps"); + commandLine.add("--no-heading"); + commandLine.add("-p"); + commandLine.add(String.valueOf(pid)); + return commandLine; + } + + String processStdout(final String outStr) { + final String [] output = outStr.split(" "); + return output[output.length - 1]; + } + @Override public String getProcessName(Integer pid) { String result = null; - List<String> commandLine = new ArrayList<>(); - commandLine.add("ps"); - commandLine.add("--no-heading"); - commandLine.add("-p"); - commandLine.add(String.valueOf(pid)); + final List<String> commandLine = buildCommandLine(pid); try { Process process = createAndRunProcess(commandLine); BufferedReader reader = getProcessOutput(process); + if (!IS_UNIX) reader.readLine(); // skip header line result = reader.readLine(); - if (result != null) { - String [] output = result.split(" "); - result = output[output.length - 1]; - } + if (result != null) + result = processStdout(result); } catch (IOException | ApplicationException e) { - logger.log(Level.WARNING, "can't run ps!", e); + logger.log(Level.WARNING, "can't run '" + commandLine.get(0) + "'!", e); } return result;
--- a/unix-process-handler/src/test/java/com/redhat/thermostat/service/internal/UnixProcessUtilitiesTest.java Tue Sep 20 13:57:38 2016 -0400 +++ b/unix-process-handler/src/test/java/com/redhat/thermostat/service/internal/UnixProcessUtilitiesTest.java Wed Sep 21 11:41:25 2016 -0400 @@ -42,6 +42,7 @@ import java.util.ArrayList; import java.util.List; +import com.redhat.thermostat.service.process.UNIXProcessHandler; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -51,37 +52,60 @@ public class UnixProcessUtilitiesTest { + private static final boolean IS_UNIX = !System.getProperty("os.name").contains("Windows"); + private BufferedReader reader; private BufferedReader emptyReader; private List<String> processArguments = new ArrayList<>(); - private UnixProcessUtilities process; + private UNIXProcessHandler process; @Before public void setUp() { - - String data = "123 fluff"; + + String data = IS_UNIX ? "123 fluff" : "headerline\r\n\"fluff.exe\",\"1868\",\"Console\",\"1\",\"25,952 K\""; reader = new BufferedReader(new StringReader(data)); emptyReader = new BufferedReader(new StringReader("")); - + processArguments.clear(); - process = new UnixProcessUtilities() { - @Override - public Process createAndRunProcess(List<String> args) - throws IOException { - processArguments.addAll(args); - return null; - } - - @Override - void exec(String command) { - processArguments.add(command); - } - - public java.io.BufferedReader getProcessOutput(Process process) { - return reader; + + if (IS_UNIX) { + process = new UnixProcessUtilities() { + @Override + public Process createAndRunProcess(List<String> args) + throws IOException { + processArguments.addAll(args); + return null; + } + + @Override + void exec(String command) { + processArguments.add(command); + } + + public java.io.BufferedReader getProcessOutput(Process process) { + return reader; + } }; - }; + } else { + process = new UnixProcessUtilities.WindowsProcessUtilities() { + @Override + public Process createAndRunProcess(List<String> args) + throws IOException { + processArguments.addAll(args); + return null; + } + + @Override + void exec(String command) { + processArguments.add(command); + } + + public java.io.BufferedReader getProcessOutput(Process process) { + return reader; + } + }; + } } @Test @@ -107,29 +131,52 @@ String result = process.getProcessName(12345); Assert.assertEquals("fluff", result); - Assert.assertTrue(processArguments.contains("12345")); - - Assert.assertTrue(processArguments.contains("ps")); - Assert.assertTrue(processArguments.contains("--no-heading")); - Assert.assertTrue(processArguments.contains("-p")); + + if (IS_UNIX) { + Assert.assertTrue(processArguments.contains("12345")); + Assert.assertTrue(processArguments.contains("ps")); + Assert.assertTrue(processArguments.contains("--no-heading")); + Assert.assertTrue(processArguments.contains("-p")); + } + else { + Assert.assertTrue(processArguments.contains("\"PID eq 12345\"")); + Assert.assertTrue(processArguments.contains("tasklist")); + } } @Test public void getProcessNameNoOutput() { // redefine, since we need an empty reader - UnixProcessUtilities process = new UnixProcessUtilities() { - @Override - public Process createAndRunProcess(List<String> args) - throws IOException { - processArguments.addAll(args); - return null; - } - - public java.io.BufferedReader getProcessOutput(Process process) { - return emptyReader; + final UNIXProcessHandler process; + if (IS_UNIX) { + process = new UnixProcessUtilities() { + @Override + public Process createAndRunProcess(List<String> args) + throws IOException { + processArguments.addAll(args); + return null; + } + + public java.io.BufferedReader getProcessOutput(Process process) { + return emptyReader; + } }; - }; + } + else { + process = new UnixProcessUtilities.WindowsProcessUtilities() { + @Override + public Process createAndRunProcess(List<String> args) + throws IOException { + processArguments.addAll(args); + return null; + } + + public java.io.BufferedReader getProcessOutput(Process process) { + return emptyReader; + } + }; + } String result = process.getProcessName(12345); Assert.assertNull(result);
--- a/web/endpoint-plugin/web-service/src/main/java/com/redhat/thermostat/web/endpoint/internal/JettyContainerLauncher.java Tue Sep 20 13:57:38 2016 -0400 +++ b/web/endpoint-plugin/web-service/src/main/java/com/redhat/thermostat/web/endpoint/internal/JettyContainerLauncher.java Wed Sep 21 11:41:25 2016 -0400 @@ -192,7 +192,8 @@ tempWebDefaults.deleteOnExit(); writeWebDefaults(tempWebDefaults, uri); - ctx.setDefaultsDescriptor(tempWebDefaults.getAbsolutePath()); + // Jetty/OpenJDK requires this string to be a forward-slash separated path, even on windows + ctx.setDefaultsDescriptor("file://"+tempWebDefaults.getAbsolutePath().replace('\\','/')); // Make server startup fail if context cannot be deployed. // Please don't change this.