From c0207cffac20b0f60f38c903cc121c96b1958f96 Mon Sep 17 00:00:00 2001 From: Ajay Kannan Date: Wed, 22 Jun 2016 20:13:11 -0700 Subject: [PATCH 1/2] Ignore comments between XML tags and add XML parsing tests. (#292) Ignore comments between XML tags and remove extraneous method in XmlUtils --- .../config/AppEngineWebXmlProcessor.java | 52 +- .../apphosting/utils/config/XmlUtils.java | 20 +- .../config/AppEngineWebXmlProcessorTest.java | 2076 +++++++++++++++++ .../config/AppEngineWebXmlReaderTest.java | 226 ++ .../utils/config/AppEngineWebXmlTest.java | 1171 ++++++++++ .../apphosting/utils/config/XmlUtilsTest.java | 94 + .../config/AutoIdPolicy_appengine-web.xml | 6 + .../ClassLoaderConfig_appengine-web.xml | 15 + .../utils/config/Empty_appengine-web.xml | 9 + .../utils/config/Minimal_appengine-web.xml | 5 + .../config/NoThreadsafe_appengine-web.xml | 4 + .../config/UrlStreamHandler_appengine-web.xml | 6 + .../utils/config/Valid_appengine-web.xml | 13 + .../apphosting/utils/config/appengine-web.xsd | 279 +++ 14 files changed, 3931 insertions(+), 45 deletions(-) create mode 100644 appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/AppEngineWebXmlProcessorTest.java create mode 100644 appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/AppEngineWebXmlReaderTest.java create mode 100644 appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/AppEngineWebXmlTest.java create mode 100644 appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/XmlUtilsTest.java create mode 100644 appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/AutoIdPolicy_appengine-web.xml create mode 100644 appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/ClassLoaderConfig_appengine-web.xml create mode 100644 appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/Empty_appengine-web.xml create mode 100644 appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/Minimal_appengine-web.xml create mode 100644 appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/NoThreadsafe_appengine-web.xml create mode 100644 appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/UrlStreamHandler_appengine-web.xml create mode 100644 appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/Valid_appengine-web.xml create mode 100644 appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/appengine-web.xsd diff --git a/appengine-managed-runtime/src/main/java/com/google/apphosting/utils/config/AppEngineWebXmlProcessor.java b/appengine-managed-runtime/src/main/java/com/google/apphosting/utils/config/AppEngineWebXmlProcessor.java index d638a34..290847d 100644 --- a/appengine-managed-runtime/src/main/java/com/google/apphosting/utils/config/AppEngineWebXmlProcessor.java +++ b/appengine-managed-runtime/src/main/java/com/google/apphosting/utils/config/AppEngineWebXmlProcessor.java @@ -231,48 +231,48 @@ private void processSecondLevelNode(Element elt, AppEngineWebXml appEngineWebXml } private void processApplicationNode(Element node, AppEngineWebXml appEngineWebXml) { - appEngineWebXml.setAppId(getTextNode(node)); + appEngineWebXml.setAppId(XmlUtils.getText(node)); } private void processPublicRootNode(Element node, AppEngineWebXml appEngineWebXml) { - appEngineWebXml.setPublicRoot(getTextNode(node)); + appEngineWebXml.setPublicRoot(XmlUtils.getText(node)); } private void processVersionNode(Element node, AppEngineWebXml appEngineWebXml) { - appEngineWebXml.setMajorVersionId(getTextNode(node)); + appEngineWebXml.setMajorVersionId(XmlUtils.getText(node)); } private void processSourceLanguageNode(Element node, AppEngineWebXml appEngineWebXml) { - appEngineWebXml.setSourceLanguage(getTextNode(node)); + appEngineWebXml.setSourceLanguage(XmlUtils.getText(node)); } private void processModuleNode(Element node, AppEngineWebXml appEngineWebXml) { - appEngineWebXml.setModule(getTextNode(node)); + appEngineWebXml.setModule(XmlUtils.getText(node)); } private void processServiceNode(Element node, AppEngineWebXml appEngineWebXml) { - appEngineWebXml.setService(getTextNode(node)); + appEngineWebXml.setService(XmlUtils.getText(node)); } private void processInstanceClassNode(Element node, AppEngineWebXml appEngineWebXml) { - appEngineWebXml.setInstanceClass(getTextNode(node)); + appEngineWebXml.setInstanceClass(XmlUtils.getText(node)); } private String getChildNodeText(Element parentNode, String childTag) { - String result = null; Element node = XmlUtils.getOptionalChildElement(parentNode, childTag); - if (node != null) { - result = XmlUtils.getBody(node); + if (node == null) { + return null; } - return result; + String result = XmlUtils.getText(node); + return result.isEmpty() ? null : result; } private Integer getChildNodePositiveInteger(Element parentNode, String childTag) { Integer result = null; Element node = XmlUtils.getOptionalChildElement(parentNode, childTag); - if (node != null && XmlUtils.getBody(node) != null) { - String trimmedText = (XmlUtils.getBody(node)).trim(); + if (node != null) { + String trimmedText = XmlUtils.getText(node); if (!trimmedText.isEmpty()) { try { result = Integer.parseInt(trimmedText); @@ -291,8 +291,8 @@ private Integer getChildNodePositiveInteger(Element parentNode, String childTag) private Double getChildNodeDouble(Element parentNode, String childTag) { Double result = null; Element node = XmlUtils.getOptionalChildElement(parentNode, childTag); - if (node != null && XmlUtils.getBody(node) != null) { - String trimmedText = (XmlUtils.getBody(node)).trim(); + if (node != null) { + String trimmedText = XmlUtils.getText(node); if (!trimmedText.isEmpty()) { try { result = Double.parseDouble(trimmedText); @@ -407,7 +407,7 @@ private void processThreadsafeNode(Element node, AppEngineWebXml appEngineWebXml } private void processAutoIdPolicyNode(Element node, AppEngineWebXml appEngineWebXml) { - appEngineWebXml.setAutoIdPolicy(getTextNode(node)); + appEngineWebXml.setAutoIdPolicy(XmlUtils.getText(node)); } private void processCodeLockNode(Element node, AppEngineWebXml appEngineWebXml) { @@ -419,7 +419,7 @@ private void processVmNode(Element node, AppEngineWebXml appEngineWebXml) { } private void processEnvNode(Element node, AppEngineWebXml appEngineWebXml) { - appEngineWebXml.setEnv(getTextNode(node)); + appEngineWebXml.setEnv(XmlUtils.getText(node)); } private void processFilesetNode(Element node, AppEngineWebXml appEngineWebXml, FileType type) { @@ -521,7 +521,7 @@ private void processNetworkNode(Element settingsNode, AppEngineWebXml appEngineW network.setInstanceTag(instanceTag); } for (Element subNode : getNodeIterable(settingsNode, "forwarded-port")) { - String forwardedPort = getTextNode(subNode); + String forwardedPort = XmlUtils.getText(subNode); network.addForwardedPort(forwardedPort); } String name = trim(getChildNodeText(settingsNode, "name")); @@ -558,7 +558,7 @@ private void processPermissionsNode(Element node, AppEngineWebXml appEngineWebXm private void processInboundServicesNode(Element node, AppEngineWebXml appEngineWebXml) { for (Element subNode : getNodeIterable(node, "service")) { - String service = getTextNode(subNode); + String service = XmlUtils.getText(subNode); appEngineWebXml.addInboundService(service); } } @@ -591,7 +591,7 @@ private void processApiConfigNode(Element node, AppEngineWebXml appEngineWebXml) appEngineWebXml.setApiConfig(new ApiConfig(servlet, url)); for (Element subNode : getNodeIterable(node, "endpoint-servlet-mapping-id")) { - String id = trim(getTextNode(subNode)); + String id = XmlUtils.getText(subNode); if (id != null && id.length() > 0) { appEngineWebXml.addApiEndpoint(id); } @@ -615,11 +615,11 @@ private void processClassPathPrioritySpecifier(Element node, ClassLoaderConfig c } private void processUrlStreamHandler(Element node, AppEngineWebXml appEngineWebXml) { - appEngineWebXml.setUrlStreamHandlerType(getTextNode(node)); + appEngineWebXml.setUrlStreamHandlerType(XmlUtils.getText(node)); } private boolean getBooleanValue(Element node) { - return toBoolean(getTextNode(node)); + return toBoolean(XmlUtils.getText(node)); } private boolean getBooleanAttributeValue(Element node, String attribute) { @@ -631,14 +631,6 @@ private boolean toBoolean(String value) { return (value.equalsIgnoreCase("true") || value.equals("1")); } - private String getTextNode(Element node) { - String value = XmlUtils.getBody(node); - if (value == null) { - value = ""; - } - return value.trim(); - } - private String trim(String attribute) { return attribute == null ? null : attribute.trim(); } diff --git a/appengine-managed-runtime/src/main/java/com/google/apphosting/utils/config/XmlUtils.java b/appengine-managed-runtime/src/main/java/com/google/apphosting/utils/config/XmlUtils.java index 8330ec1..d94455a 100644 --- a/appengine-managed-runtime/src/main/java/com/google/apphosting/utils/config/XmlUtils.java +++ b/appengine-managed-runtime/src/main/java/com/google/apphosting/utils/config/XmlUtils.java @@ -153,7 +153,11 @@ static String getOptionalChildElementBody(Element element, String tagName) { static String getChildElementBody(Element element, String tagName, boolean required) { Element elt = getChildElement(element, tagName, required); - return (elt != null) ? getBody(elt) : null; + if (elt == null) { + return null; + } + String result = getText(elt); + return result.isEmpty() ? null : result; } static Element getOptionalChildElement(Element parent, String tagName) { @@ -181,20 +185,6 @@ static String getAttributeOrNull(Element element, String name) { } } - static String getBody(Element element) { - NodeList nodes = element.getChildNodes(); - if (nodes == null || nodes.getLength() == 0) { - return null; - } - - Node firstNode = nodes.item(0); - if (firstNode == null) { - return null; - } - - return firstNode.getNodeValue(); - } - static List getChildren(Element element) { return getChildren(element, null); } diff --git a/appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/AppEngineWebXmlProcessorTest.java b/appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/AppEngineWebXmlProcessorTest.java new file mode 100644 index 0000000..92bbf3c --- /dev/null +++ b/appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/AppEngineWebXmlProcessorTest.java @@ -0,0 +1,2076 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed 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 com.google.apphosting.utils.config; + +import com.google.apphosting.utils.config.AppEngineWebXml.AdminConsolePage; +import com.google.apphosting.utils.config.AppEngineWebXml.ApiConfig; +import com.google.apphosting.utils.config.AppEngineWebXml.ErrorHandler; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * Unit tests for {@link AppEngineWebXmlProcessor}. + */ +public class AppEngineWebXmlProcessorTest extends TestCase { + + private static final String XML_FORMAT = + " " + + "helloworld-java " + + ""; + + private static final String XML_FORMAT_SESSIONS_SSL = + " " + + "helloworld-java " + + "1 " + + "true" + + "true" + + ""; + + private AppEngineWebXml getAppEngineWebXml(String content) { + File schemaFile = + new File("src/test/resources/com/google/apphosting/utils/config/appengine-web.xsd"); + XmlUtils.validateXmlContent(content, schemaFile); + return new AppEngineWebXmlProcessor() + .processXml(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))); + } + + public void testParseGoodXml() { + AppEngineWebXml aewx = getAppEngineWebXml(XML_FORMAT); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId(null); + expected.setSslEnabled(true); + assertEquals(expected, aewx); + } + + private AppEngineWebXml newAppEngineWebXml() { + AppEngineWebXml result = new AppEngineWebXml(); + result.setWarmupRequestsEnabled(true); + return result; + } + + public void testParseSessionSslXml() { + AppEngineWebXml aewx = getAppEngineWebXml(XML_FORMAT_SESSIONS_SSL); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setSslEnabled(true); + expected.setSessionsEnabled(true); + assertEquals(expected, aewx); + } + + public void testAsyncSessionPersistence() { + doTestAsyncSessionPersistence("queue-name=\"blar\"", "blar"); + } + + public void testAsyncSessionPersistence_NoQueueName() { + doTestAsyncSessionPersistence("", null); + } + + private void doTestAsyncSessionPersistence(String queueNameXml, String expectedQueueName) { + String xml = String.format( + " " + + "helloworld-java " + + "1 " + + "true" + + "" + + "", queueNameXml); + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + assertFalse(expected.getAsyncSessionPersistence()); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setSslEnabled(true); + expected.setSessionsEnabled(true); + expected.setAsyncSessionPersistence(true); + expected.setAsyncSessionPersistenceQueueName(expectedQueueName); + assertEquals(expected, aewx); + } + + public void testSslDisabled() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "false" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setSslEnabled(false); + assertEquals(expected, aewx); + } + + public void testServerAndInstance() { + String xml = + "\n" + + "helloworld-java\n" + + "service1\n" + + "1\n" + + "F4\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setService("service1"); + expected.setInstanceClass("F4"); + assertEquals(expected, aewx); + } + + public void testModuleIsAllowed() { + String xml = + "\n" + + "helloworld-java\n" + + "service1\n" + + "1\n" + + "F4\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setModule("service1"); + expected.setInstanceClass("F4"); + assertEquals(expected, aewx); + assertEquals("service1", aewx.getModule()); + } + + public void testErrorWhenServiceAndModuleAreDefined() { + String xml = + "\n" + + "helloworld-java\n" + + "service1\n" + + "service2\n" + + "1\n" + + "F4\n" + + "\n"; + + try { + getAppEngineWebXml(xml); + fail("Expected AppEngineConfigException."); + } catch (AppEngineConfigException ex) { + // expected + } + } + + public void testParseUserPermissions() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.addUserPermission("com.foo.FooPermission", "foo", null); + expected.addUserPermission("com.foo.FooPermission", "bar", "baz,qux"); + assertEquals(expected, aewx); + } + + public void testParseInvalidUserPermissions() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "\n" + + "\n" + + "\n"; + + try { + getAppEngineWebXml(xml); + fail("Expected AppEngineConfigException."); + } catch (AppEngineConfigException ex) { + // expected + } + } + + public void testParseBadXml() { + String badXml = + " " + + "" + + ""; + + try { + getAppEngineWebXml(badXml); + fail("expected rte"); + } catch (AppEngineConfigException rte) { + // good + } + } + + public void testParsePublicRoot() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "/foobar\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setPublicRoot("/foobar"); + assertEquals(expected, aewx); + } + + public void testParseInvalidPublicRoot() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "*.html\n" + + "\n"; + + try { + getAppEngineWebXml(xml); + fail("Expected AppEngineConfigException."); + } catch (AppEngineConfigException ex) { + // expected + } + } + + public void testParseNoInboundServices() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertEquals(Collections.singleton("warmup"), aewx.getInboundServices()); + } + + public void testParseOneInboundService() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " xmpp_message\n" + + "\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + Set expected = new HashSet<>(); + expected.add("warmup"); + expected.add("xmpp_message"); + assertEquals(expected, aewx.getInboundServices()); + } + + public void testParseTwoInboundServices() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " mail\n" + + " xmpp_message\n" + + "\n" + + "\n"; + + Set expected = new HashSet<>(); + expected.add("warmup"); + expected.add("mail"); + expected.add("xmpp_message"); + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertEquals(expected, aewx.getInboundServices()); + } + + public void testStaticInclude() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \n" + + " \n" + + "\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertEquals(2, aewx.getStaticFileIncludes().size()); + Iterator it = aewx.getStaticFileIncludes().iterator(); + assertEquals(new AppEngineWebXml.StaticFileInclude("**.nocache.**", "8d"), it.next()); + assertEquals(new AppEngineWebXml.StaticFileInclude("**", null), it.next()); + } + + public void testStaticIncludeWithZeroExpiration() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \n" + + " \n" + + "\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertEquals(2, aewx.getStaticFileIncludes().size()); + Iterator it = aewx.getStaticFileIncludes().iterator(); + assertEquals(new AppEngineWebXml.StaticFileInclude("**.nocache.**", "0"), it.next()); + assertEquals(new AppEngineWebXml.StaticFileInclude("**", null), it.next()); + } + + public void testStaticIncludeWithHttpHeaders() { + String xml = + "\n" + + "all-your-base\n" + + "1\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + "\n"; + AppEngineWebXml aewx = getAppEngineWebXml(xml); + + assertEquals(1, aewx.getStaticFileIncludes().size()); + Map httpHeaders = aewx.getStaticFileIncludes().get(0).getHttpHeaders(); + assertEquals(2, httpHeaders.size()); + assertEquals("somebody", httpHeaders.get("Bomb-Planter")); + assertEquals("no", httpHeaders.get("Chance-To-Survive")); + } + + public void testPrecompilationEnabled() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "true" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setPrecompilationEnabled(true); + assertEquals(expected, aewx); + } + + public void testThreadsafe() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "true" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + assertTrue(aewx.getThreadsafeValueProvided()); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setThreadsafe(true); + assertEquals(expected, aewx); + } + + public void testAutoIdPolicy() { + for (String policy : Arrays.asList("legacy", "default")) { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "" + policy + "" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setAutoIdPolicy(policy); + assertEquals(expected, aewx); + } + } + + public void testCodeLock() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "true" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setCodeLock(true); + assertEquals(expected, aewx); + } + + public void testVmRuntime() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "true" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setUseVm(true); + assertEquals(expected, aewx); + } + + public void testEnv() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "1" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setEnv("1"); + assertEquals(expected, aewx); + } + + public void testBetaSettings() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "true\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setUseVm(true); + expected.addBetaSetting("machine_type", "n1-standard-1"); + expected.addBetaSetting("health_check_enabled", "on"); + expected.addBetaSetting("health_check_restart_timeout", "600"); + expected.addBetaSetting("root_setup_command", "/bin/bash {app_name}/setup.sh"); + assertEquals(expected, aewx); + } + + public void testAdminConsolePages() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "" + + "" + + "" + + "" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.addAdminConsolePage(new AdminConsolePage("foo", "/bar")); + expected.addAdminConsolePage(new AdminConsolePage("baz", "/bax")); + assertEquals(expected, aewx); + } + + public void testErrorHandlers() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "" + + "" + + "" + + "" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.addErrorHandler(new ErrorHandler("ohno.html", null)); + expected.addErrorHandler(new ErrorHandler("timeout.html", "timeout")); + assertEquals(expected, aewx); + } + + public void testWarmupRequestsDefault() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + assertTrue(aewx.getWarmupRequestsEnabled()); + assertEquals(Collections.singleton("warmup"), aewx.getInboundServices()); + } + + public void testWarmupRequestsTrue() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "true\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + assertTrue(aewx.getWarmupRequestsEnabled()); + assertEquals(Collections.singleton("warmup"), aewx.getInboundServices()); + } + + public void testWarmupRequestsFalse() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "false\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + assertFalse(aewx.getWarmupRequestsEnabled()); + assertEquals(Collections.emptySet(), aewx.getInboundServices()); + } + + public void testApiConfig() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setApiConfig(new ApiConfig("foo.ConfigServlet", "/my/api")); + assertEquals(expected, aewx); + } + + public void testApiEndpoint() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "endpoint-1\n" + + "\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setApiConfig(new ApiConfig("foo.ConfigServlet", "/my/api")); + expected.addApiEndpoint("endpoint-1"); + assertTrue(aewx.isApiEndpoint("endpoint-1")); + assertEquals(expected, aewx); + } + + public void testMultipleApiEndpoints() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "endpoint-1\n" + + "endpoint-foo\n" + + "endpoint-bar\n" + + "\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setApiConfig(new ApiConfig("foo.ConfigServlet", "/my/api")); + expected.addApiEndpoint("endpoint-1"); + assertTrue(aewx.isApiEndpoint("endpoint-1")); + expected.addApiEndpoint("endpoint-foo"); + assertTrue(aewx.isApiEndpoint("endpoint-foo")); + expected.addApiEndpoint("endpoint-bar"); + assertTrue(aewx.isApiEndpoint("endpoint-bar")); + assertEquals(expected, aewx); + } + + public void testWhitespaceIsTrimmed() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n\n \n/root\n \n \n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + assertEquals("/root", aewx.getPublicRoot()); + } + + private AppEngineWebXml.AutomaticScaling parseAndGetAutomaticScaling(String xml) { + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + assertNotNull(aewx.getAutomaticScaling()); + AppEngineWebXml.AutomaticScaling settings = aewx.getAutomaticScaling(); + assertTrue(aewx.getManualScaling().isEmpty()); + assertTrue(aewx.getBasicScaling().isEmpty()); + return settings; + } + + public void testAutomaticScaling_empty() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertTrue(settings.isEmpty()); + assertNull(settings.getMinPendingLatency()); + assertNull(settings.getMaxPendingLatency()); + assertNull(settings.getMaxIdleInstances()); + assertNull(settings.getMinIdleInstances()); + assertNull(settings.getMinNumInstances()); + assertNull(settings.getMaxNumInstances()); + assertNull(settings.getCoolDownPeriodSec()); + assertNull(settings.getCpuUtilization()); + } + + public void testAutomaticScaling_idleInstancesAutomatic() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " automatic\n" + + " automatic\n" + + "\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertFalse(settings.isEmpty()); + assertEquals("automatic", settings.getMinIdleInstances()); + assertEquals("automatic", settings.getMaxIdleInstances()); + } + + public void testAutomaticScaling_idleInstances() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 2\n" + + " 3\n" + + "\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertFalse(settings.isEmpty()); + assertEquals("2", settings.getMinIdleInstances()); + assertEquals("3", settings.getMaxIdleInstances()); + } + + public void testAutomaticScaling_pendingLatencyAutomatic() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " automatic\n" + + " automatic\n" + + "\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertFalse(settings.isEmpty()); + assertEquals("automatic", settings.getMinPendingLatency()); + assertEquals("automatic", settings.getMaxPendingLatency()); + } + + public void testAutomaticScaling_pendingLatency() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 2.2s\n" + + " 3000ms\n" + + "\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertFalse(settings.isEmpty()); + assertEquals("2.2s", settings.getMinPendingLatency()); + assertEquals("3000ms", settings.getMaxPendingLatency()); + } + + public void testAutomaticScaling_maxConcurrentRequests() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 20\n" + + "\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertFalse(settings.isEmpty()); + assertEquals("20", settings.getMaxConcurrentRequests()); + } + + public void testAutomaticScaling_minNumInstances_empty() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail("Should fail due to incorrect min-num-instances"); + } catch (AppEngineConfigException expected) { + } + + xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail("Should fail due to incorrect min-num-instances"); + } catch (AppEngineConfigException expected) { + } + + xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \t \n \n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail("Should fail due to incorrect min-num-instances"); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_minNumInstances_valid() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 2\n" + + "\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertFalse(settings.isEmpty()); + assertEquals(2, settings.getMinNumInstances().intValue()); + } + + public void testAutomaticScaling_minNumInstances_zero() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 0\n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_minNumInstances_negative() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " -2\n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_minNumInstances_string() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 123abc\n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_maxNumInstances_empty() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertTrue(settings.isEmpty()); + assertNull(settings.getMaxNumInstances()); + } + + public void testAutomaticScaling_maxNumInstances_zero() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 0\n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_maxNumInstances_valid() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 15\n" + + "\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertFalse(settings.isEmpty()); + assertEquals(15, settings.getMaxNumInstances().intValue()); + } + + public void testAutomaticScaling_maxNumInstances_negative() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " -5\n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_maxNumInstances_string() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 123abc\n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_coolDownPeriodSec_empty() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertTrue(settings.isEmpty()); + assertNull(settings.getCoolDownPeriodSec()); + + xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "\n" + + "\n"; + settings = parseAndGetAutomaticScaling(xml); + assertTrue(settings.isEmpty()); + assertNull(settings.getCoolDownPeriodSec()); + } + + public void testAutomaticScaling_coolDownPeriodSec_valid() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 25\n" + + "\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertFalse(settings.isEmpty()); + assertEquals(25, settings.getCoolDownPeriodSec().intValue()); + } + + public void testAutomaticScaling_coolDownPeriodSec_zero() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 0\n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_coolDownPeriodSec_negative() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " -25\n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_coolDownPeriodSec_string() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 123abc\n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_cpuUtilization_empty() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertTrue(settings.isEmpty()); + assertNull(settings.getCpuUtilization()); + } + + public void testAutomaticScaling_cpuUtilization_targetUtilization_empty() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertTrue(settings.isEmpty()); + assertNull(settings.getCpuUtilization()); + } + + public void testAutomaticScaling_cpuUtilization_targetUtilization_valid() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \n" + + " 0.5\n" + + " \n" + + "\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertFalse(settings.isEmpty()); + assertEquals(0.5, settings.getCpuUtilization().getTargetUtilization().doubleValue()); + } + + public void testAutomaticScaling_cpuUtilization_targetUtilization_zero() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \n" + + " 0\n" + + " \n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_cpuUtilization_targetUtilization_negative() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \n" + + " -0.5\n" + + " \n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_cpuUtilization_targetUtilization_moreThanOne() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \n" + + " 1.1\n" + + " \n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_cpuUtilization_targetUtilization_string() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \n" + + " 0.5adc\n" + + " \n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_cpuUtilization_aggregationWindowLengthSec_empty() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertTrue(settings.isEmpty()); + assertNull(settings.getCpuUtilization()); + } + + public void testAutomaticScaling_cpuUtilization_aggregationWindowLengthSec_valid() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \n" + + " 35\n" + + " \n" + + "\n" + + "\n"; + AppEngineWebXml.AutomaticScaling settings = parseAndGetAutomaticScaling(xml); + assertFalse(settings.isEmpty()); + assertEquals(35, settings.getCpuUtilization().getAggregationWindowLengthSec().intValue()); + } + + public void testAutomaticScaling_cpuUtilization_aggregationWindowLengthSec_zero() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \n" + + " 0\n" + + " \n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_cpuUtilization_aggregationWindowLengthSec_negative() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \n" + + " -5\n" + + " \n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testAutomaticScaling_cpuUtilization_aggregationWindowLengthSec_string() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \n" + + " 5abc\n" + + " \n" + + "\n" + + "\n"; + try { + parseAndGetAutomaticScaling(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + private AppEngineWebXml.ManualScaling parseAngGetManualScaling(String xml) { + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + assertNotNull(aewx.getManualScaling()); + AppEngineWebXml.ManualScaling settings = aewx.getManualScaling(); + assertTrue(aewx.getAutomaticScaling().isEmpty()); + assertTrue(aewx.getBasicScaling().isEmpty()); + return settings; + } + + public void testManualScaling_empty() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \n" + + "\n" + + "\n"; + AppEngineWebXml.ManualScaling settings = parseAngGetManualScaling(xml); + assertTrue(settings.isEmpty()); + assertNull(settings.getInstances()); + } + + public void testManualScaling_instances() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 10\n" + + "\n" + + "\n"; + AppEngineWebXml.ManualScaling settings = parseAngGetManualScaling(xml); + assertFalse(settings.isEmpty()); + assertEquals("10", settings.getInstances()); + } + + private AppEngineWebXml.BasicScaling parseAngGetBasicScaling(String xml) { + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + assertNotNull(aewx.getBasicScaling()); + AppEngineWebXml.BasicScaling settings = aewx.getBasicScaling(); + assertTrue(aewx.getAutomaticScaling().isEmpty()); + assertTrue(aewx.getManualScaling().isEmpty()); + return settings; + } + + public void testBasicScaling_empty() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " \n" + + " \n" + + "\n" + + "\n"; + AppEngineWebXml.BasicScaling settings = parseAngGetBasicScaling(xml); + assertTrue(settings.isEmpty()); + assertNull(settings.getIdleTimeout()); + } + + public void testBasicScaling_maxInstances() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 11\n" + + "\n" + + "\n"; + AppEngineWebXml.BasicScaling settings = parseAngGetBasicScaling(xml); + assertFalse(settings.isEmpty()); + assertEquals("11", settings.getMaxInstances()); + assertNull(settings.getIdleTimeout()); + } + + public void testBasicScaling_maxInstancesAndIdleTimeout() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 11\n" + + " 10m\n" + + "\n" + + "\n"; + AppEngineWebXml.BasicScaling settings = parseAngGetBasicScaling(xml); + assertFalse(settings.isEmpty()); + assertEquals("11", settings.getMaxInstances()); + assertEquals("10m", settings.getIdleTimeout()); + } + + private AppEngineWebXml.HealthCheck parseAngGetHealthCheck(String xml) { + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + assertNotNull(aewx.getHealthCheck()); + AppEngineWebXml.HealthCheck settings = aewx.getHealthCheck(); + return settings; + } + + public void testHealthCheck_noSetting() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n"; + AppEngineWebXml.HealthCheck settings = parseAngGetHealthCheck(xml); + assertTrue(settings.isEmpty()); + assertTrue(settings.getEnableHealthCheck()); + assertNull(settings.getCheckIntervalSec()); + assertNull(settings.getTimeoutSec()); + assertNull(settings.getUnhealthyThreshold()); + assertNull(settings.getHealthyThreshold()); + assertNull(settings.getRestartThreshold()); + assertNull(settings.getHost()); + } + + public void testHealthCheck_emptySetting() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "\n" + + "\n"; + AppEngineWebXml.HealthCheck settings = parseAngGetHealthCheck(xml); + assertTrue(settings.isEmpty()); + assertTrue(settings.getEnableHealthCheck()); + assertNull(settings.getCheckIntervalSec()); + assertNull(settings.getTimeoutSec()); + assertNull(settings.getUnhealthyThreshold()); + assertNull(settings.getHealthyThreshold()); + assertNull(settings.getRestartThreshold()); + assertNull(settings.getHost()); + } + + public void testHealthCheck_enableHealthCheck_noSetting() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "\n" + + "\n"; + AppEngineWebXml.HealthCheck settings = parseAngGetHealthCheck(xml); + assertTrue(settings.isEmpty()); + assertTrue(settings.getEnableHealthCheck()); + assertNull(settings.getCheckIntervalSec()); + assertNull(settings.getTimeoutSec()); + assertNull(settings.getUnhealthyThreshold()); + assertNull(settings.getHealthyThreshold()); + assertNull(settings.getRestartThreshold()); + assertNull(settings.getHost()); + } + + public void testHealthCheck_enableHealthCheck_empty() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "\n" + + "\n"; + AppEngineWebXml.HealthCheck settings = parseAngGetHealthCheck(xml); + assertTrue(settings.isEmpty()); + assertTrue(settings.getEnableHealthCheck()); + assertNull(settings.getCheckIntervalSec()); + assertNull(settings.getTimeoutSec()); + assertNull(settings.getUnhealthyThreshold()); + assertNull(settings.getHealthyThreshold()); + assertNull(settings.getRestartThreshold()); + assertNull(settings.getHost()); + } + + public void testHealthCheck_enableHealthCheck_false() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " false\n" + + "\n" + + "\n"; + AppEngineWebXml.HealthCheck settings = parseAngGetHealthCheck(xml); + assertFalse(settings.isEmpty()); + assertFalse(settings.getEnableHealthCheck()); + assertNull(settings.getCheckIntervalSec()); + assertNull(settings.getTimeoutSec()); + assertNull(settings.getUnhealthyThreshold()); + assertNull(settings.getHealthyThreshold()); + assertNull(settings.getRestartThreshold()); + assertNull(settings.getHost()); + } + + public void testHealthCheck_enableHealthCheck_true() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " true\n" + + "\n" + + "\n"; + AppEngineWebXml.HealthCheck settings = parseAngGetHealthCheck(xml); + assertTrue(settings.isEmpty()); + assertTrue(settings.getEnableHealthCheck()); + assertNull(settings.getCheckIntervalSec()); + assertNull(settings.getTimeoutSec()); + assertNull(settings.getUnhealthyThreshold()); + assertNull(settings.getHealthyThreshold()); + assertNull(settings.getRestartThreshold()); + assertNull(settings.getHost()); + } + + public void testHealthCheck_getCheckIntervalSec() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 5\n" + + "\n" + + "\n"; + AppEngineWebXml.HealthCheck settings = parseAngGetHealthCheck(xml); + assertFalse(settings.isEmpty()); + assertTrue(settings.getEnableHealthCheck()); + assertEquals(5, settings.getCheckIntervalSec().intValue()); + assertNull(settings.getTimeoutSec()); + assertNull(settings.getUnhealthyThreshold()); + assertNull(settings.getHealthyThreshold()); + assertNull(settings.getRestartThreshold()); + assertNull(settings.getHost()); + } + + public void testHealthCheck_getCheckIntervalSec_invalid() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 123abc\n" + + "\n" + + "\n"; + + try { + parseAngGetHealthCheck(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testHealthCheck_getCheckIntervalSec_negative() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " -1\n" + + "\n" + + "\n"; + + try { + parseAngGetHealthCheck(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testHealthCheck_getTimeoutSec() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 6\n" + + "\n" + + "\n"; + AppEngineWebXml.HealthCheck settings = parseAngGetHealthCheck(xml); + assertFalse(settings.isEmpty()); + assertTrue(settings.getEnableHealthCheck()); + assertNull(settings.getCheckIntervalSec()); + assertEquals(6, settings.getTimeoutSec().intValue()); + assertNull(settings.getUnhealthyThreshold()); + assertNull(settings.getHealthyThreshold()); + assertNull(settings.getRestartThreshold()); + assertNull(settings.getHost()); + } + + public void testHealthCheck_getTimeoutSec_invalid() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 123abc\n" + + "\n" + + "\n"; + + try { + parseAngGetHealthCheck(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testHealthCheck_getTimeoutSec_negative() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " -1\n" + + "\n" + + "\n"; + + try { + parseAngGetHealthCheck(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testHealthCheck_getUnhealthyThreshold() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 7\n" + + "\n" + + "\n"; + AppEngineWebXml.HealthCheck settings = parseAngGetHealthCheck(xml); + assertFalse(settings.isEmpty()); + assertTrue(settings.getEnableHealthCheck()); + assertNull(settings.getCheckIntervalSec()); + assertNull(settings.getTimeoutSec()); + assertEquals(7, settings.getUnhealthyThreshold().intValue()); + assertNull(settings.getHealthyThreshold()); + assertNull(settings.getRestartThreshold()); + assertNull(settings.getHost()); + } + + public void testHealthCheck_getUnhealthyThreshold_invalid() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 123abc\n" + + "\n" + + "\n"; + + try { + parseAngGetHealthCheck(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testHealthCheck_getUnhealthyThreshold_negative() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " -1\n" + + "\n" + + "\n"; + + try { + parseAngGetHealthCheck(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testHealthCheck_getHealthyThreshold() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 8\n" + + "\n" + + "\n"; + AppEngineWebXml.HealthCheck settings = parseAngGetHealthCheck(xml); + assertFalse(settings.isEmpty()); + assertTrue(settings.getEnableHealthCheck()); + assertNull(settings.getCheckIntervalSec()); + assertNull(settings.getTimeoutSec()); + assertNull(settings.getUnhealthyThreshold()); + assertEquals(8, settings.getHealthyThreshold().intValue()); + assertNull(settings.getRestartThreshold()); + assertNull(settings.getHost()); + } + + public void testHealthCheck_getHealthyThreshold_invalid() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 123abc\n" + + "\n" + + "\n"; + + try { + parseAngGetHealthCheck(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testHealthCheck_getHealthyThreshold_negative() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " -1\n" + + "\n" + + "\n"; + + try { + parseAngGetHealthCheck(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testHealthCheck_getRestartThreshold() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 9\n" + + "\n" + + "\n"; + AppEngineWebXml.HealthCheck settings = parseAngGetHealthCheck(xml); + assertFalse(settings.isEmpty()); + assertTrue(settings.getEnableHealthCheck()); + assertNull(settings.getCheckIntervalSec()); + assertNull(settings.getTimeoutSec()); + assertNull(settings.getUnhealthyThreshold()); + assertNull(settings.getHealthyThreshold()); + assertEquals(9, settings.getRestartThreshold().intValue()); + assertNull(settings.getHost()); + } + + public void testHealthCheck_getRestartThreshold_invalid() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " 123abc\n" + + "\n" + + "\n"; + + try { + parseAngGetHealthCheck(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testHealthCheck_getRestartThreshold_negative() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " -1\n" + + "\n" + + "\n"; + + try { + parseAngGetHealthCheck(xml); + fail(); + } catch (AppEngineConfigException expected) { + } + } + + public void testHealthCheck_getHost() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " test.com\n" + + "\n" + + "\n"; + AppEngineWebXml.HealthCheck settings = parseAngGetHealthCheck(xml); + assertFalse(settings.isEmpty()); + assertTrue(settings.getEnableHealthCheck()); + assertNull(settings.getCheckIntervalSec()); + assertNull(settings.getTimeoutSec()); + assertNull(settings.getUnhealthyThreshold()); + assertNull(settings.getHealthyThreshold()); + assertNull(settings.getRestartThreshold()); + assertEquals("test.com", settings.getHost()); + } + + public void testHealthCheck_allSettings() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + " false\n" + + " 5\n" + + " 6\n" + + " 7\n" + + " 8\n" + + " 9\n" + + " test.com\n" + + "\n" + + "\n"; + AppEngineWebXml.HealthCheck settings = parseAngGetHealthCheck(xml); + assertFalse(settings.isEmpty()); + assertFalse(settings.getEnableHealthCheck()); + assertEquals(5, settings.getCheckIntervalSec().intValue()); + assertEquals(6, settings.getTimeoutSec().intValue()); + assertEquals(7, settings.getUnhealthyThreshold().intValue()); + assertEquals(8, settings.getHealthyThreshold().intValue()); + assertEquals(9, settings.getRestartThreshold().intValue()); + assertEquals("test.com", settings.getHost()); + } + + private AppEngineWebXml.Resources parseAndGetResources(String xml) { + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + assertNotNull(aewx.getResources()); + AppEngineWebXml.Resources resources = aewx.getResources(); + return resources; + } + + private AppEngineWebXml.Network parseAndGetNetwork(String xml) { + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + assertNotNull(aewx.getNetwork()); + AppEngineWebXml.Network network = aewx.getNetwork(); + return network; + } + + public void testResources_NoSetting() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n"; + AppEngineWebXml.Resources resources = parseAndGetResources(xml); + assertTrue(resources.isEmpty()); + } + + public void testResources_EmptySetting() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "\n" + + "\n"; + AppEngineWebXml.Resources resources = parseAndGetResources(xml); + assertTrue(resources.isEmpty()); + } + + public void testResources_Cpu() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "2.5\n" + + "\n" + + "\n"; + AppEngineWebXml.Resources resources = parseAndGetResources(xml); + assertEquals(resources.getCpu(), 2.5); + } + + public void testResources_MemoryGb() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "25\n" + + "\n" + + "\n"; + AppEngineWebXml.Resources resources = parseAndGetResources(xml); + assertEquals(resources.getMemoryGb(), 25.0); + } + + public void testResources_DiskSizeGb() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "250\n" + + "\n" + + "\n"; + AppEngineWebXml.Resources resources = parseAndGetResources(xml); + assertEquals(resources.getDiskSizeGb(), 250); + } + + public void testResources_Full() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "2.5\n" + + "25\n" + + "250\n" + + "\n" + + "\n"; + AppEngineWebXml.Resources resources = parseAndGetResources(xml); + assertEquals(resources.getCpu(), 2.5); + assertEquals(resources.getMemoryGb(), 25.0); + assertEquals(resources.getDiskSizeGb(), 250); + } + + public void testNetwork_NoSetting() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n"; + AppEngineWebXml.Network network = parseAndGetNetwork(xml); + assertTrue(network.isEmpty()); + } + + public void testNetwork_EmptySetting() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "\n" + + "\n"; + AppEngineWebXml.Network network = parseAndGetNetwork(xml); + assertTrue(network.isEmpty()); + } + + public void testNetwork_InstanceTag() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "mytag\n" + + "\n" + + "\n"; + AppEngineWebXml.Network network = parseAndGetNetwork(xml); + assertEquals(network.getInstanceTag(), "mytag"); + } + + public void testNetwork_ForwardedPorts() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "myport1" + + "myport2" + + "myport3:myport4" + + "\n" + + "\n"; + AppEngineWebXml.Network network = parseAndGetNetwork(xml); + assertEquals(network.getForwardedPorts().size(), 3); + assertEquals(network.getForwardedPorts().get(0), "myport1"); + assertEquals(network.getForwardedPorts().get(1), "myport2"); + assertEquals(network.getForwardedPorts().get(2), "myport3:myport4"); + } + + public void testNetwork_Name() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "mynetwork" + + "\n" + + "\n"; + AppEngineWebXml.Network network = parseAndGetNetwork(xml); + assertEquals(network.getName(), "mynetwork"); + } + + public void testNetwork_Full() { + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "\n" + + "myport1" + + "myport2" + + "mytag\n" + + "mynetwork" + + "\n" + + "\n"; + AppEngineWebXml.Network network = parseAndGetNetwork(xml); + assertEquals(network.getForwardedPorts().size(), 2); + assertEquals(network.getForwardedPorts().get(0), "myport1"); + assertEquals(network.getForwardedPorts().get(1), "myport2"); + assertEquals(network.getName(), "mynetwork"); + } + + private void runConflictingScalingTest(String xml) throws Exception { + String fullXml = + "\n" + + "helloworld-java\n" + + "1\n" + + xml + + "\n"; + try { + getAppEngineWebXml(fullXml); + fail("AppEngineConfigException expected."); + } catch (AppEngineConfigException e) { + assertTrue(e.toString().contains("only one of 'automatic-scaling',")); + } + } + + public void testConflictingScaling_AutomaticBasic() throws Exception { + String xml = + "\n" + + " 11\n" + + "\n" + + "\n" + + " 11\n" + + "\n"; + runConflictingScalingTest(xml); + } + + public void testConflictingScaling_ManualAutomatic() throws Exception { + String xml = + "\n" + + " 11\n" + + "\n" + + "\n" + + " 11\n" + + "\n"; + runConflictingScalingTest(xml); + } + + public void testConflictingScaling_ManualBasic() throws Exception { + String xml = + "\n" + + " 11\n" + + "\n" + + "\n" + + " 11\n" + + "\n"; + runConflictingScalingTest(xml); + } + + private AppEngineWebXml parseUrlStreamHandlerType(String streamHandlerType) { + String xml = + "\n" + + "gianni-app\n" + + "1\n" + + "" + streamHandlerType + "\n" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + return aewx; + } + + public void testUrlStreamHandler() { + assertEquals("native", parseUrlStreamHandlerType("native").getUrlStreamHandlerType()); + assertEquals("urlfetch", parseUrlStreamHandlerType("urlfetch").getUrlStreamHandlerType()); + assertTrue(parseUrlStreamHandlerType("native").toString() + .contains("urlStreamHandlerType=native")); + try { + parseUrlStreamHandlerType("fail here"); + fail("Expected an AppEngineConfigException exception."); + } catch (AppEngineConfigException e) { + // pass + } + } + + public void testUseGoogleConnectorJ() { + // Enabled explicitly + String xml = + "\n" + + "helloworld-java\n" + + "1\n" + + "true" + + "\n"; + + AppEngineWebXml aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + AppEngineWebXml expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setUseGoogleConnectorJ(true); + assertEquals(expected, aewx); + + // Disabled explicitly + xml = "\n" + + "helloworld-java\n" + + "1\n" + + "false" + + "\n"; + + aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + expected.setUseGoogleConnectorJ(false); + assertEquals(expected, aewx); + + // No set in the XML + xml = "\n" + + "helloworld-java\n" + + "1\n" + + "\n"; + + aewx = getAppEngineWebXml(xml); + assertNotNull(aewx); + expected = newAppEngineWebXml(); + expected.setAppId("helloworld-java"); + expected.setMajorVersionId("1"); + assertEquals(expected, aewx); + } +} diff --git a/appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/AppEngineWebXmlReaderTest.java b/appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/AppEngineWebXmlReaderTest.java new file mode 100644 index 0000000..ef8cc82 --- /dev/null +++ b/appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/AppEngineWebXmlReaderTest.java @@ -0,0 +1,226 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed 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 com.google.apphosting.utils.config; + +import com.google.apphosting.utils.config.AppEngineWebXml.ClassLoaderConfig; +import com.google.apphosting.utils.config.AppEngineWebXml.PrioritySpecifierEntry; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.List; +import java.util.logging.Filter; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +/** + * Unit tests for {@link AppEngineWebXmlReader}. + */ +public class AppEngineWebXmlReaderTest extends TestCase { + + private static final String APP_ENGINE_WEB_XML_FORMAT = + "src/test/resources/%s/%s_appengine-web.xml"; + + private String getFilenameToParse(String testName) { + String packagePath = getClass().getPackage().getName().replace('.', '/'); + return String.format(APP_ENGINE_WEB_XML_FORMAT, packagePath, testName); + } + + public void testNonexistentFileThrowsAppEngineConfigException() { + AppEngineWebXmlReader reader = new AppEngineWebXmlReader(".") { + @Override + public String getFilename() { + return "does not exist"; + } + }; + + try { + reader.readAppEngineWebXml(); + fail("expected AppEngineConfigException"); + } catch (AppEngineConfigException e) { + // good + } + } + + public void testExplosiveXmlProcessingThrowsAppEngineConfigException() { + AppEngineWebXmlReader reader = new AppEngineWebXmlReader(".") { + + @Override + protected InputStream getInputStream() { + return new ByteArrayInputStream(new byte[0]); + } + + @Override + protected AppEngineWebXml processXml(InputStream is) { + throw new RuntimeException("ka-boom"); + } + }; + try { + reader.readAppEngineWebXml(); + fail("expected AppEngineConfigException"); + } catch (AppEngineConfigException e) { + // good + assertEquals("ka-boom", e.getCause().getMessage()); + } + } + + public void testValid() { + AppEngineWebXmlReader reader = new AppEngineWebXmlReader(".") { + @Override + public String getFilename() { + return getFilenameToParse("Valid"); + } + }; + + AppEngineWebXml aeWebXml = reader.readAppEngineWebXml(); + assertEquals("myapp", aeWebXml.getAppId()); + assertEquals("1", aeWebXml.getMajorVersionId()); + assertTrue(aeWebXml.getSslEnabled()); + assertTrue(aeWebXml.getThreadsafeValueProvided()); + } + + public void testClassLoaderConfig() { + AppEngineWebXmlReader reader = new AppEngineWebXmlReader(".") { + @Override + public String getFilename() { + return getFilenameToParse("ClassLoaderConfig"); + } + }; + + AppEngineWebXml aeWebXml = reader.readAppEngineWebXml(); + ClassLoaderConfig config = aeWebXml.getClassLoaderConfig(); + assertNotNull(config); + List entries = config.getEntries(); + assertNotNull(entries); + assertEquals(1, entries.size()); + PrioritySpecifierEntry entry = entries.get(0); + assertEquals("mailapi.jar", entry.getFilename()); + assertEquals(-1.0d, entry.getPriority()); + } + + public void testUrlStreamHandlerDefaultConfig() { + AppEngineWebXmlReader reader = new AppEngineWebXmlReader(".") { + @Override + public String getFilename() { + return getFilenameToParse("Minimal"); + } + }; + + AppEngineWebXml aeWebXml = reader.readAppEngineWebXml(); + assertNull(aeWebXml.getUrlStreamHandlerType()); + } + + public void testUrlStreamHandlerNativeConfig() { + AppEngineWebXmlReader reader = new AppEngineWebXmlReader(".") { + @Override + public String getFilename() { + return getFilenameToParse("UrlStreamHandler"); + } + }; + + AppEngineWebXml aeWebXml = reader.readAppEngineWebXml(); + assertEquals("native", aeWebXml.getUrlStreamHandlerType()); + } + + public void testEmptyAppId() { + AppEngineWebXmlReader reader = new AppEngineWebXmlReader(".") { + @Override + public String getFilename() { + return getFilenameToParse("Empty"); + } + }; + + AppEngineWebXml aeWebXml = reader.readAppEngineWebXml(); + assertEquals("", aeWebXml.getAppId()); + assertEquals("", aeWebXml.getMajorVersionId()); + assertFalse(aeWebXml.getSslEnabled()); + } + + public void testMinimal() { + AppEngineWebXmlReader reader = new AppEngineWebXmlReader(".") { + @Override + public String getFilename() { + return getFilenameToParse("Minimal"); + } + }; + + AppEngineWebXml aeWebXml = reader.readAppEngineWebXml(); + assertEquals("myapp", aeWebXml.getAppId()); + assertEquals("1", aeWebXml.getMajorVersionId()); + } + + public void testMissingThreadsafeElement() { + // First test the default behavior, which is that threadsafe is required. + AppEngineWebXmlReader reader = new AppEngineWebXmlReader(".") { + @Override + public String getFilename() { + return getFilenameToParse("NoThreadsafe"); + } + }; + + try { + reader.readAppEngineWebXml(); + } catch (AppEngineConfigException aece) { + assertTrue(aece.getMessage().startsWith( + "appengine-web.xml does not contain a element.")); + } + + // Now test customized behavior, where we allow a missing threadsafe element. + reader = new AppEngineWebXmlReader(".") { + @Override + public String getFilename() { + return getFilenameToParse("NoThreadsafe"); + } + + @Override + protected boolean allowMissingThreadsafeElement() { + return true; + } + }; + AppEngineWebXml aeWebXml = reader.readAppEngineWebXml(); + assertEquals("myapp", aeWebXml.getAppId()); + assertEquals("1", aeWebXml.getMajorVersionId()); + assertFalse(aeWebXml.getThreadsafeValueProvided()); + } + + public void testAutoIdPolicyWarning() { + AppEngineWebXmlReader reader = new AppEngineWebXmlReader(".") { + @Override + public String getFilename() { + return getFilenameToParse("AutoIdPolicy"); + } + }; + class WarningDetector implements Filter { + boolean warned = false; + + @Override public boolean isLoggable(LogRecord record) { + warned = warned || record.getMessage().indexOf("Legacy auto ids are deprecated") >= 0; + return true; + } + } + + WarningDetector warningDetector = new WarningDetector(); + Logger.getLogger(AppEngineWebXmlReader.class.getName()).setFilter(warningDetector); + + AppEngineWebXml aeWebXml = reader.readAppEngineWebXml(); + assertEquals("myapp", aeWebXml.getAppId()); + assertEquals("1", aeWebXml.getMajorVersionId()); + assertEquals("legacy", aeWebXml.getAutoIdPolicy()); + assertTrue("No deprecation warning logged for legacy auto ids", warningDetector.warned); + } +} \ No newline at end of file diff --git a/appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/AppEngineWebXmlTest.java b/appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/AppEngineWebXmlTest.java new file mode 100644 index 0000000..7e37746 --- /dev/null +++ b/appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/AppEngineWebXmlTest.java @@ -0,0 +1,1171 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed 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 com.google.apphosting.utils.config; + +import static org.junit.Assert.assertNotEquals; + +import com.google.apphosting.utils.config.AppEngineWebXml.ScalingType; + +import junit.framework.TestCase; + +import java.util.HashMap; +import java.util.Map; + +public class AppEngineWebXmlTest extends TestCase { + + private static final String WHITE_SPACE = "\t \n"; + private static final String EMPTY = ""; + private static final String AUTOMATIC = "automatic"; + + public void testApiEndpointExplicit() { + AppEngineWebXml xml = new AppEngineWebXml(); + xml.addApiEndpoint("foo"); + assertTrue(xml.isApiEndpoint("foo")); + assertFalse(xml.isApiEndpoint("bar")); + } + + public void testThreadsafeTracking() { + AppEngineWebXml xml = new AppEngineWebXml(); + assertFalse(xml.getThreadsafeValueProvided()); + xml.setThreadsafe(false); + assertTrue(xml.getThreadsafeValueProvided()); + + xml = new AppEngineWebXml(); + xml.setThreadsafe(true); + assertTrue(xml.getThreadsafeValueProvided()); + } + + public void testStaticFileIncludeEqualsNoExpires() { + AppEngineWebXml.StaticFileInclude include1 = new AppEngineWebXml.StaticFileInclude( + "/static-files/*", null); + + // Things should equal themselves at least. + assertEquals(include1, include1); + assertEquals(include1.hashCode(), include1.hashCode()); + assertFalse(include1.equals(null)); + assertFalse(include1.equals("should not be equal")); + + // Things could be equal to other things. + AppEngineWebXml.StaticFileInclude include2 = new AppEngineWebXml.StaticFileInclude( + "/static-files/*", null); + assertEquals(include1, include2); + assertEquals(include2, include1); + assertEquals(include1.hashCode(), include2.hashCode()); + + // A different path should make two StaticFileInclude instances NOT equal. + AppEngineWebXml.StaticFileInclude include3 = new AppEngineWebXml.StaticFileInclude( + "/moar-static-files/*", null); + assertFalse(include1.equals(include3)); + assertFalse(include3.equals(include1)); + // Hash codes being equal is just "almost certainly" wrong (as opposed to definitely wrong). + assertTrue(include1.hashCode() != include3.hashCode()); + + // null pattern + AppEngineWebXml.StaticFileInclude include4 = new AppEngineWebXml.StaticFileInclude(null, null); + assertFalse(include1.equals(include4)); + assertFalse(include4.equals(include1)); + // Hash codes being equal is just "almost certainly" wrong (as opposed to definitely wrong). + assertTrue(include1.hashCode() != include4.hashCode()); + + // Different HTTP headers should make two StaticFileIncludes not equal. + include2.getHttpHeaders().put("Name", "value"); + assertFalse(include1.equals(include2)); + assertFalse(include2.equals(include1)); + assertTrue(include1.hashCode() != include2.hashCode()); + + // StaticFileIncludes can have HTTP headers and still be equal. + include1.getHttpHeaders().put("Name", "value"); + assertEquals(include1, include2); + assertEquals(include2, include1); + assertEquals(include1.hashCode(), include2.hashCode()); + } + + public void testStaticFileIncludeEqualsWithExpires() { + String expiration = "7d"; + AppEngineWebXml.StaticFileInclude include1 = new AppEngineWebXml.StaticFileInclude( + "/static-files/*", expiration); + AppEngineWebXml.StaticFileInclude include2 = new AppEngineWebXml.StaticFileInclude( + "/static-files/*", expiration); + + // Things should equal themselves at least. + assertEquals(include1, include1); + assertEquals(include1.hashCode(), include1.hashCode()); + + // Things could be equal to other things. + assertEquals(include1, include2); + assertEquals(include2, include1); + assertEquals(include1.hashCode(), include2.hashCode()); + + // A different expiration should make two StaticFileInclude instances NOT equal. + AppEngineWebXml.StaticFileInclude include3 = new AppEngineWebXml.StaticFileInclude( + "/static-files/*", "10s"); + assertFalse(include1.equals(include3)); + assertFalse(include3.equals(include1)); + assertTrue(include1.hashCode() != include3.hashCode()); + + // A different expiration should make two StaticFileInclude instances NOT equal. + AppEngineWebXml.StaticFileInclude include4 = new AppEngineWebXml.StaticFileInclude( + "/static-files/*", null); + assertFalse(include1.equals(include4)); + assertFalse(include4.equals(include1)); + assertTrue(include1.hashCode() != include4.hashCode()); + } + + public void testStaticFileIncludeHttpHeaderOrderPreserved() { + AppEngineWebXml.StaticFileInclude include1 = new AppEngineWebXml.StaticFileInclude( + "/static-files/*", null); + Map httpHeaders = include1.getHttpHeaders(); + httpHeaders.put("foo", "1"); + httpHeaders.put("bar", "2"); + Map expected = new HashMap<>(); + expected.put("foo", "1"); + expected.put("bar", "2"); + assertEquals(expected, httpHeaders); + } + + public void testPrioritySpecifierEntryEquals() { + AppEngineWebXml.PrioritySpecifierEntry entry1 = new AppEngineWebXml.PrioritySpecifierEntry(); + AppEngineWebXml.PrioritySpecifierEntry entry2 = new AppEngineWebXml.PrioritySpecifierEntry(); + assertEquals(entry1, entry2); + assertNotNull(entry1); + entry1.setFilename("mailapi.jar"); + assertNotEquals(entry1, entry2); + entry2.setFilename("mailapi.jar"); + assertEquals(entry1, entry2); + entry1.setPriority("1.0"); + assertNotEquals(entry1, entry2); + entry2.setPriority("1.0"); + assertEquals(entry1, entry2); + } + + + public void testPrioritySpecifierEntry() { + AppEngineWebXml.PrioritySpecifierEntry entry = new AppEngineWebXml.PrioritySpecifierEntry(); + assertEquals(1.0d, entry.getPriorityValue()); + assertNull(entry.getPriority()); + entry.setFilename("mailapi.jar"); + try { + entry.setFilename("another.jar"); + fail(); + } catch (AppEngineConfigException e) { + // OK + } + } + + public void testClassLoaderConfigEquals() { + AppEngineWebXml.PrioritySpecifierEntry ps = new AppEngineWebXml.PrioritySpecifierEntry(); + ps.setFilename("mailapi.jar"); + + AppEngineWebXml.ClassLoaderConfig config1 = new AppEngineWebXml.ClassLoaderConfig(); + AppEngineWebXml.ClassLoaderConfig config2 = new AppEngineWebXml.ClassLoaderConfig(); + assertEquals(config1, config2); + assertNotNull(config1); + config1.add(ps); + assertNotEquals(config1, config2); + config2.add(ps); + assertEquals(config1, config2); + assertEquals(1, config1.getEntries().size()); + assertEquals(ps, config1.getEntries().get(0)); + + AppEngineWebXml xml1 = new AppEngineWebXml(); + AppEngineWebXml xml2 = new AppEngineWebXml(); + xml1.setClassLoaderConfig(config1); + assertNotEquals(xml1, xml2); + xml2.setClassLoaderConfig(config1); + assertEquals(xml1, xml2); + } + + public void testClassLoaderConfigToStringAndHash() { + AppEngineWebXml.PrioritySpecifierEntry ps = new AppEngineWebXml.PrioritySpecifierEntry(); + ps.setFilename("mailapi.jar"); + AppEngineWebXml.ClassLoaderConfig config = new AppEngineWebXml.ClassLoaderConfig(); + config.add(ps); + AppEngineWebXml xml = new AppEngineWebXml(); + int hash = xml.hashCode(); + String str = xml.toString(); + xml.setClassLoaderConfig(config); + assertNotEquals(str, xml.toString()); + assertNotEquals(hash, xml.hashCode()); + } + + public void testInstanceClass() { + AppEngineWebXml xml = new AppEngineWebXml(); + xml.setInstanceClass(WHITE_SPACE); + assertNull(xml.getInstanceClass()); + xml.setInstanceClass(EMPTY); + assertNull(xml.getInstanceClass()); + xml.setInstanceClass("F8"); + assertEquals("F8", xml.getInstanceClass()); + xml.setInstanceClass("d4"); + assertEquals("d4", xml.getInstanceClass()); + xml.setInstanceClass(null); + assertNull(xml.getInstanceClass()); + } + + public void testAutomaticScaling_setMinPendingLatency() { + AppEngineWebXml.AutomaticScaling settings = new AppEngineWebXml.AutomaticScaling(); + settings.setMinPendingLatency(WHITE_SPACE); + assertNull(settings.getMinPendingLatency()); + settings.setMinPendingLatency(EMPTY); + assertNull(settings.getMinPendingLatency()); + assertTrue(settings.isEmpty()); + settings.setMinPendingLatency(AUTOMATIC); + assertEquals(AUTOMATIC, settings.getMinPendingLatency()); + assertFalse(settings.isEmpty()); + settings.setMinPendingLatency(null); + assertNull(settings.getMinPendingLatency()); + assertTrue(settings.isEmpty()); + } + + public void testAutomaticScaling_setMaxPendingLatency() { + AppEngineWebXml.AutomaticScaling settings = new AppEngineWebXml.AutomaticScaling(); + settings.setMaxPendingLatency(WHITE_SPACE); + assertNull(settings.getMaxPendingLatency()); + settings.setMaxPendingLatency(EMPTY); + assertNull(settings.getMaxPendingLatency()); + assertTrue(settings.isEmpty()); + settings.setMaxPendingLatency(AUTOMATIC); + assertEquals(AUTOMATIC, settings.getMaxPendingLatency()); + assertFalse(settings.isEmpty()); + settings.setMaxPendingLatency(null); + assertNull(settings.getMaxPendingLatency()); + assertTrue(settings.isEmpty()); + } + + public void testAutomaticScaling_setMinIdleInstances() { + AppEngineWebXml.AutomaticScaling settings = new AppEngineWebXml.AutomaticScaling(); + settings.setMinIdleInstances(WHITE_SPACE); + assertNull(settings.getMinIdleInstances()); + settings.setMinIdleInstances(EMPTY); + assertNull(settings.getMinIdleInstances()); + assertTrue(settings.isEmpty()); + settings.setMinIdleInstances(AUTOMATIC); + assertEquals(AUTOMATIC, settings.getMinIdleInstances()); + assertFalse(settings.isEmpty()); + settings.setMinIdleInstances(null); + assertNull(settings.getMinIdleInstances()); + assertTrue(settings.isEmpty()); + } + + public void testAutomaticScaling_setMaxIdleInstances() { + AppEngineWebXml.AutomaticScaling settings = new AppEngineWebXml.AutomaticScaling(); + settings.setMaxIdleInstances(WHITE_SPACE); + assertNull(settings.getMaxIdleInstances()); + settings.setMaxIdleInstances(EMPTY); + assertNull(settings.getMaxIdleInstances()); + assertTrue(settings.isEmpty()); + settings.setMaxIdleInstances(AUTOMATIC); + assertEquals(AUTOMATIC, settings.getMaxIdleInstances()); + assertFalse(settings.isEmpty()); + settings.setMaxIdleInstances(null); + assertNull(settings.getMaxIdleInstances()); + assertTrue(settings.isEmpty()); + } + + public void testAutomaticScaling_setMinNumInstances() { + AppEngineWebXml.AutomaticScaling settings = new AppEngineWebXml.AutomaticScaling(); + assertTrue(settings.isEmpty()); + + settings.setMinNumInstances(null); + assertNull(settings.getMinNumInstances()); + assertTrue(settings.isEmpty()); + + settings.setMinNumInstances(10); + assertEquals(10, settings.getMinNumInstances().intValue()); + assertFalse(settings.isEmpty()); + + settings.setMinNumInstances(null); + assertNull(settings.getMinNumInstances()); + assertTrue(settings.isEmpty()); + } + + public void testAutomaticScaling_setMaxNumInstances() { + AppEngineWebXml.AutomaticScaling settings = new AppEngineWebXml.AutomaticScaling(); + assertTrue(settings.isEmpty()); + + settings.setMaxNumInstances(null); + assertNull(settings.getMaxNumInstances()); + assertTrue(settings.isEmpty()); + + settings.setMaxNumInstances(10); + assertEquals(10, settings.getMaxNumInstances().intValue()); + assertFalse(settings.isEmpty()); + + settings.setMaxNumInstances(null); + assertNull(settings.getMaxNumInstances()); + assertTrue(settings.isEmpty()); + } + + public void testAutomaticScaling_setCoolDownPeriodSec() { + AppEngineWebXml.AutomaticScaling settings = new AppEngineWebXml.AutomaticScaling(); + assertTrue(settings.isEmpty()); + + settings.setCoolDownPeriodSec(null); + assertNull(settings.getCoolDownPeriodSec()); + assertTrue(settings.isEmpty()); + + settings.setCoolDownPeriodSec(10); + assertEquals(10, settings.getCoolDownPeriodSec().intValue()); + assertFalse(settings.isEmpty()); + + settings.setCoolDownPeriodSec(null); + assertNull(settings.getCoolDownPeriodSec()); + assertTrue(settings.isEmpty()); + } + + public void testAutomaticScaling_setCpuUtilization() { + AppEngineWebXml.AutomaticScaling settings = new AppEngineWebXml.AutomaticScaling(); + assertTrue(settings.isEmpty()); + + settings.setCpuUtilization(null); + assertNull(settings.getCpuUtilization()); + assertTrue(settings.isEmpty()); + + AppEngineWebXml.CpuUtilization expectedCpuUtil = new AppEngineWebXml.CpuUtilization(); + expectedCpuUtil.setTargetUtilization(0.8); + expectedCpuUtil.setAggregationWindowLengthSec(10); + settings.setCpuUtilization(expectedCpuUtil); + AppEngineWebXml.CpuUtilization cpuUtil = settings.getCpuUtilization(); + assertEquals(expectedCpuUtil.getTargetUtilization(), cpuUtil.getTargetUtilization()); + assertEquals(expectedCpuUtil.getAggregationWindowLengthSec(), + cpuUtil.getAggregationWindowLengthSec()); + assertFalse(settings.isEmpty()); + + settings.setCpuUtilization(null); + assertNull(settings.getCpuUtilization()); + assertTrue(settings.isEmpty()); + } + + public void testAutomaticScaling_hashCode() { + AppEngineWebXml.AutomaticScaling settings1 = new AppEngineWebXml.AutomaticScaling(); + AppEngineWebXml.AutomaticScaling settings2 = new AppEngineWebXml.AutomaticScaling(); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + settings1.setMinIdleInstances(AUTOMATIC); + settings2.setMinIdleInstances(AUTOMATIC); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + settings1.setMaxIdleInstances("10"); + settings2.setMaxIdleInstances("10"); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + settings1.setMinPendingLatency("1s"); + settings2.setMinPendingLatency("1s"); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + settings1.setMaxPendingLatency("1.2s"); + settings2.setMaxPendingLatency("1.2s"); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + + settings1.setMinNumInstances(11); + settings2.setMinNumInstances(11); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + + settings1.setMaxNumInstances(22); + settings2.setMaxNumInstances(22); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + + settings1.setCoolDownPeriodSec(33); + settings2.setCoolDownPeriodSec(33); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + + AppEngineWebXml.CpuUtilization cpuUtil = new AppEngineWebXml.CpuUtilization(); + cpuUtil.setTargetUtilization(0.25); + cpuUtil.setAggregationWindowLengthSec(49); + + settings1.setCpuUtilization(cpuUtil); + settings2.setCpuUtilization(cpuUtil); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + } + + public void testAutomaticScaling_equals() { + AppEngineWebXml.AutomaticScaling settings1 = new AppEngineWebXml.AutomaticScaling(); + AppEngineWebXml.AutomaticScaling settings2 = new AppEngineWebXml.AutomaticScaling(); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + settings1.setMinIdleInstances(AUTOMATIC); + assertNotEquals(settings1, settings2); + settings2.setMinIdleInstances(AUTOMATIC); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + settings1.setMaxIdleInstances("1ms"); + assertNotEquals(settings1, settings2); + settings2.setMaxIdleInstances("1ms"); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + settings1.setMinPendingLatency("1s"); + assertNotEquals(settings1, settings2); + settings2.setMinPendingLatency("1s"); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + settings1.setMaxPendingLatency("1.2s"); + assertNotEquals(settings1, settings2); + settings2.setMaxPendingLatency("1.2s"); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + settings1.setMinNumInstances(5); + assertNotEquals(settings1, settings2); + settings2.setMinNumInstances(5); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + settings1.setMaxNumInstances(10); + assertNotEquals(settings1, settings2); + settings2.setMaxNumInstances(10); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + settings1.setCoolDownPeriodSec(18); + assertNotEquals(settings1, settings2); + settings2.setCoolDownPeriodSec(18); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + AppEngineWebXml.CpuUtilization cpuUtil = new AppEngineWebXml.CpuUtilization(); + cpuUtil.setTargetUtilization(0.8); + cpuUtil.setAggregationWindowLengthSec(25); + settings1.setCpuUtilization(cpuUtil); + assertNotEquals(settings1, settings2); + settings2.setCpuUtilization(cpuUtil); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + } + + public void testAutomaticScaling_toString() { + AppEngineWebXml.AutomaticScaling settings = new AppEngineWebXml.AutomaticScaling(); + assertEquals("AutomaticScaling [minPendingLatency=null, maxPendingLatency=null, " + + "minIdleInstances=null, maxIdleInstances=null, maxConcurrentRequests=null, " + + "minNumInstances=null, maxNumInstances=null, coolDownPeriodSec=null, " + + "cpuUtilization=null, " + + "targetNetworkSentBytesPerSec=null, targetNetworkSentPacketsPerSec=null, " + + "targetNetworkReceivedBytesPerSec=null, targetNetworkReceivedPacketsPerSec=null, " + + "targetDiskWriteBytesPerSec=null, targetDiskWriteOpsPerSec=null, " + + "targetDiskReadBytesPerSec=null, targetDiskReadOpsPerSec=null, " + + "targetRequestCountPerSec=null, targetConcurrentRequests=null" + + "]", + settings.toString()); + settings.setMinIdleInstances(AUTOMATIC); + assertTrue(settings.toString().contains("minIdleInstances=automatic")); + settings.setMaxIdleInstances("1ms"); + assertTrue(settings.toString().contains("maxIdleInstances=1ms")); + settings.setMinPendingLatency("1s"); + assertTrue(settings.toString().contains("minPendingLatency=1s")); + settings.setMaxPendingLatency("1.2s"); + assertTrue(settings.toString().contains("maxPendingLatency=1.2s")); + settings.setMaxConcurrentRequests("20"); + assertTrue(settings.toString().contains("maxConcurrentRequests=20")); + settings.setMinNumInstances(1); + assertTrue(settings.toString().contains("minNumInstances=1")); + settings.setMaxNumInstances(5); + assertTrue(settings.toString().contains("maxNumInstances=5")); + settings.setCoolDownPeriodSec(10); + assertTrue(settings.toString().contains("coolDownPeriodSec=10")); + + AppEngineWebXml.CpuUtilization cpuUtil = new AppEngineWebXml.CpuUtilization(); + cpuUtil.setTargetUtilization(0.8); + cpuUtil.setAggregationWindowLengthSec(25); + settings.setCpuUtilization(cpuUtil); + assertTrue(settings.toString().contains( + "cpuUtilization=CpuUtilization [targetUtilization=0.8, aggregationWindowLengthSec=25]")); + + settings.setTargetNetworkSentBytesPerSec(13); + assertTrue(settings.toString().contains("targetNetworkSentBytesPerSec=13")); + settings.setTargetNetworkSentPacketsPerSec(14); + assertTrue(settings.toString().contains("targetNetworkSentPacketsPerSec=14")); + settings.setTargetNetworkReceivedBytesPerSec(15); + assertTrue(settings.toString().contains("targetNetworkReceivedBytesPerSec=15")); + settings.setTargetNetworkReceivedPacketsPerSec(16); + assertTrue(settings.toString().contains("targetNetworkReceivedPacketsPerSec=16")); + settings.setTargetDiskWriteBytesPerSec(17); + assertTrue(settings.toString().contains("targetDiskWriteBytesPerSec=17")); + settings.setTargetDiskWriteOpsPerSec(18); + assertTrue(settings.toString().contains("targetDiskWriteOpsPerSec=18")); + settings.setTargetDiskReadBytesPerSec(19); + assertTrue(settings.toString().contains("targetDiskReadBytesPerSec=19")); + settings.setTargetDiskReadOpsPerSec(20); + assertTrue(settings.toString().contains("targetDiskReadOpsPerSec=20")); + settings.setTargetRequestCountPerSec(21); + assertTrue(settings.toString().contains("targetRequestCountPerSec=21")); + settings.setTargetConcurrentRequests(22); + assertTrue(settings.toString().contains("targetConcurrentRequests=22")); + } + + public void testCpuUtilization_setTargetUtilization() { + AppEngineWebXml.CpuUtilization settings = new AppEngineWebXml.CpuUtilization(); + assertTrue(settings.isEmpty()); + + settings.setTargetUtilization(null); + assertNull(settings.getTargetUtilization()); + assertTrue(settings.isEmpty()); + + settings.setTargetUtilization(10.0); + assertEquals(10, settings.getTargetUtilization().intValue()); + assertFalse(settings.isEmpty()); + + settings.setTargetUtilization(null); + assertNull(settings.getTargetUtilization()); + assertTrue(settings.isEmpty()); + } + + public void testCpuUtilization_setAggregationWindowLengthSec() { + AppEngineWebXml.CpuUtilization settings = new AppEngineWebXml.CpuUtilization(); + assertTrue(settings.isEmpty()); + + settings.setAggregationWindowLengthSec(null); + assertNull(settings.getAggregationWindowLengthSec()); + assertTrue(settings.isEmpty()); + + settings.setAggregationWindowLengthSec(10); + assertEquals(10, settings.getAggregationWindowLengthSec().intValue()); + assertFalse(settings.isEmpty()); + + settings.setAggregationWindowLengthSec(null); + assertNull(settings.getAggregationWindowLengthSec()); + assertTrue(settings.isEmpty()); + } + + public void testCpuUtilization_hashCode() { + AppEngineWebXml.CpuUtilization settings1 = new AppEngineWebXml.CpuUtilization(); + AppEngineWebXml.CpuUtilization settings2 = new AppEngineWebXml.CpuUtilization(); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + settings1.setTargetUtilization(0.5); + settings2.setTargetUtilization(0.5); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + settings1.setAggregationWindowLengthSec(10); + settings2.setAggregationWindowLengthSec(10); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + } + + public void testCpuUtilization_equals() { + AppEngineWebXml.CpuUtilization settings1 = new AppEngineWebXml.CpuUtilization(); + AppEngineWebXml.CpuUtilization settings2 = new AppEngineWebXml.CpuUtilization(); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + settings1.setTargetUtilization(0.5); + assertNotEquals(settings1, settings2); + settings2.setTargetUtilization(0.5); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + settings1.setAggregationWindowLengthSec(10); + assertNotEquals(settings1, settings2); + settings2.setAggregationWindowLengthSec(10); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + } + + public void testCpuUtilization_toString() { + AppEngineWebXml.CpuUtilization settings = new AppEngineWebXml.CpuUtilization(); + assertEquals("CpuUtilization [targetUtilization=null, aggregationWindowLengthSec=null]", + settings.toString()); + + settings.setTargetUtilization(0.5); + assertEquals("CpuUtilization [targetUtilization=0.5, aggregationWindowLengthSec=null]", + settings.toString()); + + settings.setAggregationWindowLengthSec(10); + assertEquals("CpuUtilization [targetUtilization=0.5, aggregationWindowLengthSec=10]", + settings.toString()); + } + + public void testManualScaling_setInstances() { + AppEngineWebXml.ManualScaling settings = new AppEngineWebXml.ManualScaling(); + settings.setInstances(WHITE_SPACE); + assertNull(settings.getInstances()); + settings.setInstances(EMPTY); + assertNull(settings.getInstances()); + assertTrue(settings.isEmpty()); + settings.setInstances("10"); + assertEquals("10", settings.getInstances()); + assertFalse(settings.isEmpty()); + settings.setInstances(null); + assertNull(settings.getInstances()); + assertTrue(settings.isEmpty()); + } + + public void testManualScaling_hashCode() { + AppEngineWebXml.ManualScaling settings1 = new AppEngineWebXml.ManualScaling(); + AppEngineWebXml.ManualScaling settings2 = new AppEngineWebXml.ManualScaling(); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + settings1.setInstances("20"); + settings2.setInstances(settings1.getInstances()); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + } + + public void testManualScaling_equals() { + AppEngineWebXml.ManualScaling settings1 = new AppEngineWebXml.ManualScaling(); + AppEngineWebXml.ManualScaling settings2 = new AppEngineWebXml.ManualScaling(); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + settings1.setInstances("30"); + assertNotEquals(settings1, settings2); + settings1.setInstances(""); + assertEquals(settings1, settings2); + settings1.setInstances("25"); + assertNotEquals(settings1, settings2); + settings2.setInstances(settings1.getInstances()); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + } + + public void testManualScaling_toString() { + AppEngineWebXml.ManualScaling settings = new AppEngineWebXml.ManualScaling(); + assertEquals("ManualScaling [instances=null]", settings.toString()); + settings.setInstances("30"); + assertEquals("ManualScaling [instances=30]", settings.toString()); + } + + public void testBasicScaling_setMaxInstances() { + AppEngineWebXml.BasicScaling settings = new AppEngineWebXml.BasicScaling(); + settings.setMaxInstances(WHITE_SPACE); + assertNull(settings.getMaxInstances()); + settings.setMaxInstances(EMPTY); + assertNull(settings.getMaxInstances()); + assertTrue(settings.isEmpty()); + settings.setMaxInstances("10"); + assertEquals("10", settings.getMaxInstances()); + assertFalse(settings.isEmpty()); + settings.setMaxInstances(null); + assertNull(settings.getMaxInstances()); + assertTrue(settings.isEmpty()); + } + + public void testBasicScaling_setIdleTimeout() { + AppEngineWebXml.BasicScaling settings = new AppEngineWebXml.BasicScaling(); + settings.setIdleTimeout(WHITE_SPACE); + assertNull(settings.getIdleTimeout()); + settings.setIdleTimeout(EMPTY); + assertNull(settings.getIdleTimeout()); + assertTrue(settings.isEmpty()); + settings.setIdleTimeout("10m"); + assertEquals("10m", settings.getIdleTimeout()); + assertFalse(settings.isEmpty()); + settings.setIdleTimeout(null); + assertNull(settings.getIdleTimeout()); + assertTrue(settings.isEmpty()); + } + + public void testBasicScaling_hashCode() { + AppEngineWebXml.BasicScaling settings1 = new AppEngineWebXml.BasicScaling(); + AppEngineWebXml.BasicScaling settings2 = new AppEngineWebXml.BasicScaling(); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + settings1.setMaxInstances("20"); + settings2.setMaxInstances(settings1.getMaxInstances()); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + settings1.setIdleTimeout("20m"); + settings2.setIdleTimeout(settings1.getIdleTimeout()); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + } + + public void testBasicScaling_equals() { + AppEngineWebXml.BasicScaling settings1 = new AppEngineWebXml.BasicScaling(); + AppEngineWebXml.BasicScaling settings2 = new AppEngineWebXml.BasicScaling(); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + settings1.setMaxInstances("30"); + assertNotEquals(settings1, settings2); + settings1.setMaxInstances(""); + assertEquals(settings1, settings2); + settings1.setMaxInstances("25"); + assertNotEquals(settings1, settings2); + settings2.setMaxInstances(settings1.getMaxInstances()); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + settings1.setIdleTimeout("30m"); + assertNotEquals(settings1, settings2); + settings1.setIdleTimeout(""); + assertEquals(settings1, settings2); + settings1.setIdleTimeout("25m"); + assertNotEquals(settings1, settings2); + settings2.setIdleTimeout(settings1.getIdleTimeout()); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + } + + public void testBasicScaling_toString() { + AppEngineWebXml.BasicScaling settings = new AppEngineWebXml.BasicScaling(); + assertEquals("BasicScaling [maxInstances=null, idleTimeout=null]", settings.toString()); + settings.setIdleTimeout("30m"); + assertEquals("BasicScaling [maxInstances=null, idleTimeout=30m]", settings.toString()); + settings.setMaxInstances("30"); + assertEquals("BasicScaling [maxInstances=30, idleTimeout=30m]", settings.toString()); + } + + public void testHealthCheck_setEnableHealthCheck() { + AppEngineWebXml.HealthCheck settings = new AppEngineWebXml.HealthCheck(); + assertTrue(settings.getEnableHealthCheck()); + + settings.setEnableHealthCheck(false); + assertFalse(settings.getEnableHealthCheck()); + + settings.setEnableHealthCheck(true); + assertTrue(settings.getEnableHealthCheck()); + } + + public void testHealthCheck_setCheckIntervalSec() { + AppEngineWebXml.HealthCheck settings = new AppEngineWebXml.HealthCheck(); + assertTrue(settings.isEmpty()); + settings.setCheckIntervalSec(null); + assertNull(settings.getCheckIntervalSec()); + assertTrue(settings.isEmpty()); + settings.setCheckIntervalSec(10); + assertEquals(10, settings.getCheckIntervalSec().intValue()); + assertFalse(settings.isEmpty()); + settings.setCheckIntervalSec(null); + assertNull(settings.getCheckIntervalSec()); + assertTrue(settings.isEmpty()); + } + + public void testHealthCheck_setTimeoutSec() { + AppEngineWebXml.HealthCheck settings = new AppEngineWebXml.HealthCheck(); + assertTrue(settings.isEmpty()); + settings.setTimeoutSec(null); + assertNull(settings.getTimeoutSec()); + assertTrue(settings.isEmpty()); + settings.setTimeoutSec(10); + assertEquals(10, settings.getTimeoutSec().intValue()); + assertFalse(settings.isEmpty()); + settings.setTimeoutSec(null); + assertNull(settings.getTimeoutSec()); + assertTrue(settings.isEmpty()); + } + + public void testHealthCheck_setUnhealthyThreshold() { + AppEngineWebXml.HealthCheck settings = new AppEngineWebXml.HealthCheck(); + assertTrue(settings.isEmpty()); + settings.setUnhealthyThreshold(null); + assertNull(settings.getUnhealthyThreshold()); + assertTrue(settings.isEmpty()); + settings.setUnhealthyThreshold(10); + assertEquals(10, settings.getUnhealthyThreshold().intValue()); + assertFalse(settings.isEmpty()); + settings.setUnhealthyThreshold(null); + assertNull(settings.getUnhealthyThreshold()); + assertTrue(settings.isEmpty()); + } + + public void testHealthCheck_setHealthyThreshold() { + AppEngineWebXml.HealthCheck settings = new AppEngineWebXml.HealthCheck(); + assertTrue(settings.isEmpty()); + settings.setHealthyThreshold(null); + assertNull(settings.getHealthyThreshold()); + assertTrue(settings.isEmpty()); + settings.setHealthyThreshold(10); + assertEquals(10, settings.getHealthyThreshold().intValue()); + assertFalse(settings.isEmpty()); + settings.setHealthyThreshold(null); + assertNull(settings.getHealthyThreshold()); + assertTrue(settings.isEmpty()); + } + + public void testHealthCheck_setRestartThreshold() { + AppEngineWebXml.HealthCheck settings = new AppEngineWebXml.HealthCheck(); + assertTrue(settings.isEmpty()); + settings.setRestartThreshold(null); + assertNull(settings.getRestartThreshold()); + assertTrue(settings.isEmpty()); + settings.setRestartThreshold(10); + assertEquals(10, settings.getRestartThreshold().intValue()); + assertFalse(settings.isEmpty()); + settings.setRestartThreshold(null); + assertNull(settings.getRestartThreshold()); + assertTrue(settings.isEmpty()); + } + + public void testHealthCheck_setHost() { + AppEngineWebXml.HealthCheck settings = new AppEngineWebXml.HealthCheck(); + settings.setHost(WHITE_SPACE); + assertNull(settings.getHost()); + assertTrue(settings.isEmpty()); + settings.setHost(EMPTY); + assertNull(settings.getHost()); + assertTrue(settings.isEmpty()); + settings.setHost("test.com"); + assertEquals("test.com", settings.getHost()); + assertFalse(settings.isEmpty()); + settings.setHost(null); + assertNull(settings.getHost()); + assertTrue(settings.isEmpty()); + } + + public void testHealthCheck_hashCode() { + AppEngineWebXml.HealthCheck settings1 = new AppEngineWebXml.HealthCheck(); + AppEngineWebXml.HealthCheck settings2 = new AppEngineWebXml.HealthCheck(); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + + settings1.setEnableHealthCheck(true); + settings2.setEnableHealthCheck(settings1.getEnableHealthCheck()); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + + settings1.setEnableHealthCheck(false); + settings2.setEnableHealthCheck(settings1.getEnableHealthCheck()); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + + settings1.setCheckIntervalSec(5); + settings2.setCheckIntervalSec(settings1.getCheckIntervalSec()); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + + settings1.setTimeoutSec(6); + settings2.setTimeoutSec(settings1.getTimeoutSec()); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + + settings1.setUnhealthyThreshold(7); + settings2.setUnhealthyThreshold(settings1.getUnhealthyThreshold()); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + + settings1.setHealthyThreshold(8); + settings2.setHealthyThreshold(settings1.getHealthyThreshold()); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + + settings1.setRestartThreshold(9); + settings2.setRestartThreshold(settings1.getRestartThreshold()); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + + settings1.setHost("test.com"); + settings2.setHost(settings1.getHost()); + assertEquals(settings1.hashCode(), settings2.hashCode()); + assertEquals(settings1.hashCode(), settings1.hashCode()); + } + + public void testHealthCheck_equals() { + AppEngineWebXml.HealthCheck settings1 = new AppEngineWebXml.HealthCheck(); + AppEngineWebXml.HealthCheck settings2 = new AppEngineWebXml.HealthCheck(); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + // For setEnableHealthCheck. + settings2.setEnableHealthCheck(true); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + settings1.setEnableHealthCheck(false); + assertNotEquals(settings1, settings2); + assertNotEquals(settings1, settings2); + + settings2.setEnableHealthCheck(false); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + // For setCheckIntervalSec. + settings1.setCheckIntervalSec(null); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + settings1.setCheckIntervalSec(5); + assertNotEquals(settings1, settings2); + assertNotEquals(settings1, settings2); + + settings2.setCheckIntervalSec(5); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + // For setTimeoutSec. + settings1.setTimeoutSec(null); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + settings1.setTimeoutSec(6); + assertNotEquals(settings1, settings2); + assertNotEquals(settings1, settings2); + + settings2.setTimeoutSec(6); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + // For setUnhealthyThreshold. + settings1.setUnhealthyThreshold(null); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + settings1.setUnhealthyThreshold(7); + assertNotEquals(settings1, settings2); + + settings2.setUnhealthyThreshold(7); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + // For setHealthyThreshold. + settings1.setHealthyThreshold(null); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + settings1.setHealthyThreshold(8); + assertNotEquals(settings1, settings2); + assertNotEquals(settings1, settings2); + + settings2.setHealthyThreshold(8); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + // For setRestartThreshold. + settings1.setRestartThreshold(null); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + settings1.setRestartThreshold(9); + assertNotEquals(settings1, settings2); + assertNotEquals(settings1, settings2); + + settings2.setRestartThreshold(9); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + // For setHost. + settings1.setHost(""); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + settings1.setHost("test.com"); + assertNotEquals(settings1, settings2); + assertNotEquals(settings1, settings2); + + settings2.setHost("test.com"); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + } + + public void testHealthCheck_toString() { + AppEngineWebXml.HealthCheck settings = new AppEngineWebXml.HealthCheck(); + + assertEquals("HealthCheck [enableHealthCheck=true, checkIntervalSec=null, " + + "timeoutSec=null, unhealthyThreshold=null, healthyThreshold=null, " + + "restartThreshold=null, host=null]", settings.toString()); + + settings.setEnableHealthCheck(false); + assertEquals("HealthCheck [enableHealthCheck=false, checkIntervalSec=null, " + + "timeoutSec=null, unhealthyThreshold=null, healthyThreshold=null, " + + "restartThreshold=null, host=null]", settings.toString()); + + settings.setCheckIntervalSec(5); + assertEquals("HealthCheck [enableHealthCheck=false, checkIntervalSec=5, " + + "timeoutSec=null, unhealthyThreshold=null, healthyThreshold=null, " + + "restartThreshold=null, host=null]", settings.toString()); + + settings.setTimeoutSec(6); + assertEquals("HealthCheck [enableHealthCheck=false, checkIntervalSec=5, " + + "timeoutSec=6, unhealthyThreshold=null, healthyThreshold=null, " + + "restartThreshold=null, host=null]", settings.toString()); + + settings.setUnhealthyThreshold(7); + assertEquals("HealthCheck [enableHealthCheck=false, checkIntervalSec=5, " + + "timeoutSec=6, unhealthyThreshold=7, healthyThreshold=null, " + + "restartThreshold=null, host=null]", settings.toString()); + + settings.setHealthyThreshold(8); + assertEquals("HealthCheck [enableHealthCheck=false, checkIntervalSec=5, " + + "timeoutSec=6, unhealthyThreshold=7, healthyThreshold=8, " + + "restartThreshold=null, host=null]", settings.toString()); + + settings.setRestartThreshold(9); + assertEquals("HealthCheck [enableHealthCheck=false, checkIntervalSec=5, " + + "timeoutSec=6, unhealthyThreshold=7, healthyThreshold=8, " + + "restartThreshold=9, host=null]", settings.toString()); + + settings.setHost("test.com"); + assertEquals("HealthCheck [enableHealthCheck=false, checkIntervalSec=5, " + + "timeoutSec=6, unhealthyThreshold=7, healthyThreshold=8, " + + "restartThreshold=9, host=test.com]", settings.toString()); + } + + public void testResources_setCpu() { + AppEngineWebXml.Resources resources = new AppEngineWebXml.Resources(); + resources.setCpu(3.1); + assertEquals(3.1, resources.getCpu()); + } + + public void testResources_setMemoryGb() { + AppEngineWebXml.Resources resources = new AppEngineWebXml.Resources(); + resources.setMemoryGb(31.0); + assertEquals(31.0, resources.getMemoryGb()); + } + + public void testResources_setDiskSizeGb() { + AppEngineWebXml.Resources resources = new AppEngineWebXml.Resources(); + resources.setDiskSizeGb(310); + assertEquals(310, resources.getDiskSizeGb()); + } + + public void testResources_hashCode() { + AppEngineWebXml.Resources settings1 = new AppEngineWebXml.Resources(); + AppEngineWebXml.Resources settings2 = new AppEngineWebXml.Resources(); + + assertEquals(settings1.hashCode(), settings2.hashCode()); + settings1.setCpu(3.1); + settings2.setCpu(settings1.getCpu()); + assertEquals(settings1.hashCode(), settings2.hashCode()); + settings1.setMemoryGb(27.0); + settings2.setMemoryGb(settings1.getMemoryGb()); + assertEquals(settings1.hashCode(), settings2.hashCode()); + settings1.setDiskSizeGb(312); + settings2.setDiskSizeGb(settings1.getDiskSizeGb()); + assertEquals(settings1.hashCode(), settings2.hashCode()); + } + + public void testResources_equals() { + AppEngineWebXml.Resources settings1 = new AppEngineWebXml.Resources(); + AppEngineWebXml.Resources settings2 = new AppEngineWebXml.Resources(); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + // For setCpu. + settings1.setCpu(3.1); + assertNotEquals(settings1, settings2); + assertNotEquals(settings1, settings2); + + settings2.setCpu(3.1); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + // For setMemoryGb. + settings1.setMemoryGb(3.1); + assertNotEquals(settings1, settings2); + assertNotEquals(settings1, settings2); + + settings2.setMemoryGb(3.1); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + // For setDiskSizeGb. + settings1.setDiskSizeGb(31); + assertNotEquals(settings1, settings2); + assertNotEquals(settings1, settings2); + + settings2.setDiskSizeGb(31); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + } + + public void testResources_toString() { + AppEngineWebXml.Resources resources = new AppEngineWebXml.Resources(); + + assertEquals("Resources [cpu=0.0, memoryGb=0.0, " + + "diskSizeGb=0]", resources.toString()); + + resources.setCpu(1.6); + assertEquals("Resources [cpu=1.6, memoryGb=0.0, " + + "diskSizeGb=0]", resources.toString()); + + resources.setMemoryGb(5.0); + assertEquals("Resources [cpu=1.6, memoryGb=5.0, " + + "diskSizeGb=0]", resources.toString()); + resources.setDiskSizeGb(60); + assertEquals("Resources [cpu=1.6, memoryGb=5.0, " + + "diskSizeGb=60]", resources.toString()); + } + + public void testNetwork_setInstanceTag() { + AppEngineWebXml.Network network = new AppEngineWebXml.Network(); + network.setInstanceTag("mytag"); + assertEquals("mytag", network.getInstanceTag()); + } + + public void testNetwork_addForwardedPort() { + AppEngineWebXml.Network network = new AppEngineWebXml.Network(); + network.addForwardedPort("myport"); + assertEquals("myport", network.getForwardedPorts().get(0)); + } + + public void testNetwork_hashCode() { + AppEngineWebXml.Network settings1 = new AppEngineWebXml.Network(); + AppEngineWebXml.Network settings2 = new AppEngineWebXml.Network(); + + assertEquals(settings1.hashCode(), settings2.hashCode()); + settings1.setInstanceTag("mytag"); + settings2.setInstanceTag(settings1.getInstanceTag()); + assertEquals(settings1.hashCode(), settings2.hashCode()); + settings1.addForwardedPort("myport"); + settings2.addForwardedPort(settings1.getForwardedPorts().get(0)); + assertEquals(settings1.hashCode(), settings2.hashCode()); + } + + public void testNetwork_equals() { + AppEngineWebXml.Network settings1 = new AppEngineWebXml.Network(); + AppEngineWebXml.Network settings2 = new AppEngineWebXml.Network(); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + // For setInstanceTag. + settings1.setInstanceTag("mytag"); + assertNotEquals(settings1, settings2); + assertNotEquals(settings1, settings2); + + settings2.setInstanceTag("mytag"); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + + // For addForwardedPort. + settings1.addForwardedPort("myport"); + assertNotEquals(settings1, settings2); + assertNotEquals(settings1, settings2); + + settings2.addForwardedPort("myport"); + assertEquals(settings1, settings2); + assertEquals(settings1, settings1); + } + + public void testNetwork_toString() { + AppEngineWebXml.Network network = new AppEngineWebXml.Network(); + + assertEquals("Network [forwardedPorts=[], instanceTag=null]", network.toString()); + + network.setInstanceTag("mytag"); + assertEquals("Network [forwardedPorts=[], instanceTag=mytag]", network.toString()); + + network.addForwardedPort("myport"); + assertEquals("Network [forwardedPorts=[myport], instanceTag=mytag]", network.toString()); + } + + public void testGetScalingType_automatic() { + AppEngineWebXml webXml = new AppEngineWebXml(); + assertEquals(ScalingType.AUTOMATIC, webXml.getScalingType()); + } + + public void testGetScalingType_manual() { + AppEngineWebXml appEngineWebXml = new AppEngineWebXml(); + appEngineWebXml.getManualScaling().setInstances("3"); + assertEquals(ScalingType.MANUAL, appEngineWebXml.getScalingType()); + } + + public void testGetScalingType_basic() { + AppEngineWebXml appEngineWebXml = new AppEngineWebXml(); + appEngineWebXml.getBasicScaling().setMaxInstances("3"); + assertEquals(ScalingType.BASIC, appEngineWebXml.getScalingType()); + } +} \ No newline at end of file diff --git a/appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/XmlUtilsTest.java b/appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/XmlUtilsTest.java new file mode 100644 index 0000000..b3d4f49 --- /dev/null +++ b/appengine-managed-runtime/src/test/java/com/google/apphosting/utils/config/XmlUtilsTest.java @@ -0,0 +1,94 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed 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 com.google.apphosting.utils.config; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import junit.framework.TestCase; + +import org.w3c.dom.Element; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +/** + * Unit Tests for {@link XmlUtils}. + */ +public class XmlUtilsTest extends TestCase { + + private static final String VALID_XML = + "\n" + + "\n" + + " B \n" + + "\n"; + private static final String INVALID_XML = " + myapp + 1 + false + legacy + diff --git a/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/ClassLoaderConfig_appengine-web.xml b/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/ClassLoaderConfig_appengine-web.xml new file mode 100644 index 0000000..1eef99f --- /dev/null +++ b/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/ClassLoaderConfig_appengine-web.xml @@ -0,0 +1,15 @@ + + myapp + 1 + false + + + + + + + true + + + + diff --git a/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/Empty_appengine-web.xml b/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/Empty_appengine-web.xml new file mode 100644 index 0000000..5fcf2a9 --- /dev/null +++ b/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/Empty_appengine-web.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/Minimal_appengine-web.xml b/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/Minimal_appengine-web.xml new file mode 100644 index 0000000..06a3f8a --- /dev/null +++ b/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/Minimal_appengine-web.xml @@ -0,0 +1,5 @@ + + myapp + 1 + false + diff --git a/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/NoThreadsafe_appengine-web.xml b/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/NoThreadsafe_appengine-web.xml new file mode 100644 index 0000000..e8f0401 --- /dev/null +++ b/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/NoThreadsafe_appengine-web.xml @@ -0,0 +1,4 @@ + + myapp + 1 + diff --git a/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/UrlStreamHandler_appengine-web.xml b/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/UrlStreamHandler_appengine-web.xml new file mode 100644 index 0000000..72f9ebc --- /dev/null +++ b/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/UrlStreamHandler_appengine-web.xml @@ -0,0 +1,6 @@ + + myapp + 1 + false + native + diff --git a/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/Valid_appengine-web.xml b/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/Valid_appengine-web.xml new file mode 100644 index 0000000..f8f247c --- /dev/null +++ b/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/Valid_appengine-web.xml @@ -0,0 +1,13 @@ + + myapp + 1 + false + + + + + + + true + + diff --git a/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/appengine-web.xsd b/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/appengine-web.xsd new file mode 100644 index 0000000..a7fce91 --- /dev/null +++ b/appengine-managed-runtime/src/test/resources/com/google/apphosting/utils/config/appengine-web.xsd @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2dfca00ddbe12d4c793645eeea613cb2eba0f720 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Sat, 9 Jul 2016 02:47:22 +1000 Subject: [PATCH 2/2] Issue #259 Hide GAE API from webapp classpath (#290) Issue #259 Hide GAE API from webapp classpath Handles #259 by creating AppengineApiConfiguration to manage the classpath that is exposed and/or protected from the webapplication. If the GAE API is not available in the webapp, then a warning is logged and the API is added to the classpath. Currently javax.activation and javax.mail are treated as shared classes and the containers version is made available. If these are not listed, then even if they are in the added API jar, errors occur, which needs to be investigated. Currently a specific list of org.apache packages are hidden. A better approach may be to hide all org.apache classes and selectively reveals those needed for JSP, JSTL and EL support? * code cleanups * Improve unit tests for JSP/JSTL in anticipation of changes to classpath * Fixed formatting * excluded all com, javax and org packages * comment cleanup * formatting fixes * expose javax.imageio * white listed JVM provided classes * fixed review feedback * review feedback * removed old log --- .../vmruntime/VmRuntimeFileLogHandler.java | 1 + .../vmruntime/VmApiProxyDelegateTest.java | 1 + jetty9-compat-base/pom.xml | 28 +++- .../jetty9/AppengineApiConfiguration.java | 148 ++++++++++++++++++ .../jetty9/VmRuntimeWebAppContext.java | 116 ++++++++------ .../src/main/jetty-base/webapps/root.xml | 8 +- .../runtime/jetty9/SessionManagerIT.java | 42 ++++- .../vmruntime/jetty9/JettyRunner.java | 37 ++--- .../jetty9/VmRuntimeJettyKitchenSinkIT.java | 22 ++- pom.xml | 14 +- testwebapp/src/main/webapp/jstl.jsp | 15 ++ 11 files changed, 346 insertions(+), 86 deletions(-) create mode 100644 jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/AppengineApiConfiguration.java create mode 100644 testwebapp/src/main/webapp/jstl.jsp diff --git a/appengine-managed-runtime/src/main/java/com/google/apphosting/vmruntime/VmRuntimeFileLogHandler.java b/appengine-managed-runtime/src/main/java/com/google/apphosting/vmruntime/VmRuntimeFileLogHandler.java index c46eea5..0fd07a7 100644 --- a/appengine-managed-runtime/src/main/java/com/google/apphosting/vmruntime/VmRuntimeFileLogHandler.java +++ b/appengine-managed-runtime/src/main/java/com/google/apphosting/vmruntime/VmRuntimeFileLogHandler.java @@ -19,6 +19,7 @@ import com.google.apphosting.logging.JsonFormatter; import java.io.IOException; +import java.util.logging.ConsoleHandler; import java.util.logging.FileHandler; import java.util.logging.Handler; import java.util.logging.Level; diff --git a/appengine-managed-runtime/src/test/java/com/google/apphosting/vmruntime/VmApiProxyDelegateTest.java b/appengine-managed-runtime/src/test/java/com/google/apphosting/vmruntime/VmApiProxyDelegateTest.java index 41b63f2..e48987b 100644 --- a/appengine-managed-runtime/src/test/java/com/google/apphosting/vmruntime/VmApiProxyDelegateTest.java +++ b/appengine-managed-runtime/src/test/java/com/google/apphosting/vmruntime/VmApiProxyDelegateTest.java @@ -542,6 +542,7 @@ public void testApiExceptionWrapping() { exception.getMessage()); } + // TODO move to a common testing framework private static class SuppressedLogging implements Closeable { Logger logger = Logger.getLogger(VmApiProxyDelegate.class.getName()); Level level = logger.getLevel(); diff --git a/jetty9-compat-base/pom.xml b/jetty9-compat-base/pom.xml index e1df4b3..44ad539 100644 --- a/jetty9-compat-base/pom.xml +++ b/jetty9-compat-base/pom.xml @@ -28,6 +28,10 @@ jetty9-compat-base jar + + ${appengine.api.version} + + com.google.appengine @@ -74,6 +78,13 @@ jar test + + org.eclipse.jetty + apache-jstl + ${jetty.version} + jar + test + junit @@ -162,8 +173,8 @@ maven-dependency-plugin - copy-test-webapp-war - pre-integration-test + copy-wars-and-jars + generate-resources copy @@ -179,12 +190,21 @@ ${project.build.directory}/webapps/ testwebapp.war + + com.google.appengine + appengine-api-1.0-sdk + ${appengine.api.provided.version} + jar + true + ** + ${project.build.directory}/jetty-base/lib/gae/provided-api/ + unpack-deps - pre-integration-test + generate-resources unpack @@ -213,7 +233,7 @@ gae-jars - pre-integration-test + generate-resources copy-dependencies diff --git a/jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/AppengineApiConfiguration.java b/jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/AppengineApiConfiguration.java new file mode 100644 index 0000000..b2d5217 --- /dev/null +++ b/jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/AppengineApiConfiguration.java @@ -0,0 +1,148 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed 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 com.google.apphosting.vmruntime.jetty9; + +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.webapp.AbstractConfiguration; +import org.eclipse.jetty.webapp.WebAppClassLoader; +import org.eclipse.jetty.webapp.WebAppContext; + +import java.util.logging.Level; + +public class AppengineApiConfiguration extends AbstractConfiguration { + + // A class to check if the GAE API is available. + public static final String GAE_CHECK_CLASS = "com.google.appengine.api.ThreadManager"; + + // Hide the all container classes from the webapplication + // TODO update to use '.' when supported by Jetty + private static final String[] SERVER_CLASSES = { + "com.", + "javax.", + "org.", + "mozilla." + }; + + // Classes & Packages to be shared from the containers classloader + // to the webapp. Classes included here will be marked both as + // exclusions from serverclasses (not hidden from webapp) and inclusion + // to the systemclasses (cannot be overriden by the webapp) + private static final String[] SHARED_CLASSES = { + // Expose the GAE API classes + "com.google.appengine.api.LifecycleManager", + "com.google.apphosting.api.ApiProxy", + "com.google.apphosting.api.ApiStats", + "com.google.apphosting.api.CloudTrace", + "com.google.apphosting.api.CloudTraceContext", + "com.google.apphosting.api.DeadlineExceededException", + "com.google.apphosting.runtime.SessionData", + "com.google.apphosting.runtime.UncatchableError", + + // Expose the standard APIs that are provided by the container + "javax.servlet.", + "javax.el.", + "javax.mail.", // TODO Review + + // Expose the standard APIs that are provided by the JVM + "com.oracle.", + "com.sun.", + "javax.accessibility.", + "javax.activation.", + "javax.activity.", + "javax.annotation.", + "javax.imageio.", + "javax.jws.", + "javax.lang.model.", + "javax.management.", + "javax.naming.", + "javax.net.", + "javax.print.", + "javax.rmi.", + "javax.script.", + "javax.security.", + "javax.smartcardio.", + "javax.sound.", + "javax.sql.", + "javax.swing.", + "javax.tools.", + "javax.transaction.", + "javax.xml.", + "jdk.", + "org.ietf.jgss.", + "org.jcp.xml.dsig.internal.", + "org.omg.", + "org.w3c.dom.", + "org.xml.", + "sun.", + + // Expose classes needed for JSP and JSTL + "org.apache.jasper.runtime.", + "org.apache.jasper.JasperException", + "org.apache.el.ExpressionFactoryImpl", + "org.apache.tomcat.InstanceManager", + "org.apache.taglibs.", + }; + + @Override + public void preConfigure(WebAppContext context) { + for (String systemClass : SERVER_CLASSES) { + context.addServerClass(systemClass); + } + for (String gaeClass : SHARED_CLASSES) { + // Don't hide shared classes + context.prependServerClass("-" + gaeClass); + // Don't allow shared classes to be replaced by webapp + context.addSystemClass(gaeClass); + } + } + + public void configure(WebAppContext context) throws Exception { + ClassLoader loader = context.getClassLoader(); + try { + // Test if the appengine api is available + loader.loadClass(GAE_CHECK_CLASS); + } catch (Exception ex) { + if (VmRuntimeWebAppContext.logger.isLoggable(Level.FINE)) { + VmRuntimeWebAppContext.logger.log(Level.WARNING, + "No appengine API jar included in WEB-INF/lib! Please update your SDK!", ex); + } else { + VmRuntimeWebAppContext.logger.log(Level.WARNING, + "No appengine API jar included in WEB-INF/lib! Please update your SDK!"); + } + + // The appengine API is not available so we will add it and it's dependencies + Resource providedApi = + Resource.newResource( + URIUtil.addPaths(System.getProperty("jetty.base"), "/lib/gae/provided-api/")); + + if (providedApi != null) { + String[] list = providedApi.list(); + if (list != null) { + WebAppClassLoader wloader = (WebAppClassLoader) loader; + for (String jar : list) { + wloader.addClassPath(providedApi.addPath(jar)); + VmRuntimeWebAppContext.logger.log(Level.INFO, "Added " + jar + " to webapp classpath"); + } + } + } + + // Ensure the API can now be loaded + loader.loadClass(GAE_CHECK_CLASS); + } + } +} diff --git a/jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeWebAppContext.java b/jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeWebAppContext.java index 912bdc1..d984acd 100644 --- a/jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeWebAppContext.java +++ b/jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeWebAppContext.java @@ -16,7 +16,6 @@ package com.google.apphosting.vmruntime.jetty9; -import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.DatastoreServiceFactory; import com.google.appengine.api.datastore.Transaction; import com.google.appengine.api.memcache.MemcacheSerialization; @@ -44,7 +43,6 @@ import com.google.apphosting.vmruntime.VmRuntimeUtils; import com.google.apphosting.vmruntime.VmTimer; -import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.quickstart.PreconfigureDescriptorProcessor; import org.eclipse.jetty.quickstart.QuickStartDescriptorGenerator; import org.eclipse.jetty.security.ConstraintSecurityHandler; @@ -59,6 +57,8 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -80,19 +80,8 @@ public class VmRuntimeWebAppContext extends WebAppContext implements VmRuntimeTrustedAddressChecker { - private static final Logger logger = Logger.getLogger(VmRuntimeWebAppContext.class.getName()); - - // It's undesirable to have the user app override classes provided by us. - // So we mark them as Jetty system classes, which cannot be overridden. - private static final String[] SYSTEM_CLASSES = { - // The trailing dot means these are all Java packages, not individual classes. - "com.google.appengine.api.", - "com.google.appengine.tools.", - "com.google.apphosting.", - "com.google.cloud.sql.jdbc.", - "com.google.protos.cloud.sql.", - "com.google.storage.onestore.", - }; + static final Logger logger = Logger.getLogger(VmRuntimeWebAppContext.class.getName()); + // constant. If it's much larger than this we may need to // restructure the code a bit. protected static final int MAX_RESPONSE_SIZE = 32 * 1024 * 1024; @@ -100,7 +89,10 @@ public class VmRuntimeWebAppContext extends WebAppContext private final VmMetadataCache metadataCache; private final Timer wallclockTimer; private VmApiProxyEnvironment defaultEnvironment; - private DatastoreService datastoreService; + private Object contextDatastoreService; + private Method getActiveTransactions; + private Method transactionRollback; + private Method transactionGetId; private String quickstartWebXml; private PreconfigureDescriptorProcessor preconfigProcessor; @@ -122,6 +114,7 @@ public class VmRuntimeWebAppContext extends WebAppContext // because they have been executed via the SDK. private static final String[] quickstartConfigurationClasses = { org.eclipse.jetty.quickstart.QuickStartConfiguration.class.getCanonicalName(), + AppengineApiConfiguration.class.getCanonicalName(), org.eclipse.jetty.plus.webapp.EnvConfiguration.class.getCanonicalName(), org.eclipse.jetty.plus.webapp.PlusConfiguration.class.getCanonicalName(), org.eclipse.jetty.webapp.JettyWebXmlConfiguration.class.getCanonicalName() @@ -131,6 +124,7 @@ public class VmRuntimeWebAppContext extends WebAppContext // is no quickstart-web.xml. private static final String[] preconfigurationClasses = { org.eclipse.jetty.webapp.WebInfConfiguration.class.getCanonicalName(), + AppengineApiConfiguration.class.getCanonicalName(), org.eclipse.jetty.webapp.WebXmlConfiguration.class.getCanonicalName(), org.eclipse.jetty.webapp.MetaInfConfiguration.class.getCanonicalName(), org.eclipse.jetty.webapp.FragmentConfiguration.class.getCanonicalName(), @@ -153,7 +147,7 @@ public VmMetadataCache getMetadataCache() { *

If set, this context will not start, rather it will generate the * quickstart-web.xml file and then stop the server. If not set, the context will start normally *

- * + * * @param quickstartWebXml The location of the quickstart web.xml to generate */ public void setQuickstartWebXml(String quickstartWebXml) { @@ -185,8 +179,6 @@ protected void doStart() throws Exception { setConfigurationClasses(quickstartConfigurationClasses); } - datastoreService = getDatastoreService(); - if (quickstartWebXml == null) { addEventListener(new ContextListener()); } else { @@ -195,6 +187,34 @@ protected void doStart() throws Exception { } super.doStart(); + + // Look for a datastore service within the context + // reflection is needed here because the webapp will either have its + // own impl of the Datastore Service or this AppengineApiConfiguration + // will add an impl. Eitherway, it is a different instance (and maybe) + // a different version to the Datastore Service that is used by the + // container session manager. Thus we need to access the webapps + // instance, via reflection, so that we can rollback any abandoned transactions. + ClassLoader orig = Thread.currentThread().getContextClassLoader(); + try { + ClassLoader loader = getClassLoader(); + Thread.currentThread().setContextClassLoader(loader); + Class factory = loader.loadClass(DatastoreServiceFactory.class.getName()); + contextDatastoreService = factory.getMethod("getDatastoreService").invoke(null); + if (contextDatastoreService != null) { + getActiveTransactions = + contextDatastoreService.getClass().getMethod("getActiveTransactions"); + getActiveTransactions.setAccessible(true); + + Class transaction = loader.loadClass(Transaction.class.getName()); + transactionRollback = transaction.getMethod("rollback"); + transactionGetId = transaction.getMethod("getId"); + } + } catch (Exception ex) { + logger.log(Level.WARNING, "No context datastore service", ex); + } finally { + Thread.currentThread().setContextClassLoader(orig); + } } @Override @@ -225,13 +245,6 @@ protected void stopWebapp() throws Exception { } } - /** - * Broken out to facilitate testing. - */ - DatastoreService getDatastoreService() { - return DatastoreServiceFactory.getDatastoreService(); - } - /** * Creates a List of SessionStores based on the configuration in the provided AppEngineWebXml. * @@ -316,9 +329,6 @@ public void init(String appengineWebXmlFile) throws AppEngineConfigException, IO } VmRuntimeFileLogHandler.init(); - for (String systemClass : SYSTEM_CLASSES) { - addSystemClass(systemClass); - } if (appEngineWebXml == null) { // No need to configure the session manager. return; @@ -484,10 +494,7 @@ private void complete(Request baseRequest) { VmApiProxyEnvironment env = requestContext.getRequestSpecificEnvironment(); // Transaction Cleanup - Collection txns = datastoreService.getActiveTransactions(); - if (!txns.isEmpty()) { - handleAbandonedTxns(txns); - } + handleAbandonedTxns(); // Save dirty sessions HttpSession session = baseRequest.getSession(false); @@ -504,25 +511,34 @@ private void complete(Request baseRequest) { env.waitForAllApiCallsToComplete(VmRuntimeUtils.MAX_REQUEST_THREAD_API_CALL_WAIT_MS); } - void handleAbandonedTxns(Collection txns) { - // TODO(user): In the dev appserver, capture a stack trace whenever a - // transaction is started so we can print it here. - for (Transaction txn : txns) { + void handleAbandonedTxns() { + if (getActiveTransactions != null) { try { - logger.warning( - "Request completed without committing or rolling back transaction with id " - + txn.getId() - + ". Transaction will be rolled back."); - txn.rollback(); - } catch (Exception e) { - // We swallow exceptions so that there is no risk of our cleanup - // impacting the actual result of the request. + // Reflection used for reasons listed in doStart + Object txns = getActiveTransactions.invoke(contextDatastoreService); + for (Object tx : (Collection) txns) { + Object id = transactionGetId.invoke(tx); + try { + logger.warning( + "Request completed without committing or rolling back transaction " + + id + + ". Transaction will be rolled back."); + transactionRollback.invoke(tx); + } catch (InvocationTargetException ex) { + logger.log( + Level.WARNING, + "Failed to rollback abandoned transaction " + id, + ex.getTargetException()); + } catch (Exception ex) { + logger.log( + Level.WARNING, + "Failed to rollback abandoned transaction " + id, + ex); + } + } + } catch (Exception ex) { logger.log( - Level.WARNING, - "Swallowing an exception we received while trying to rollback " - + "abandoned transaction with id " - + txn.getId(), - e); + Level.WARNING, "Failed to rollback abandoned transaction", ex); } } } diff --git a/jetty9-compat-base/src/main/jetty-base/webapps/root.xml b/jetty9-compat-base/src/main/jetty-base/webapps/root.xml index 1058200..ac0c499 100644 --- a/jetty9-compat-base/src/main/jetty-base/webapps/root.xml +++ b/jetty9-compat-base/src/main/jetty-base/webapps/root.xml @@ -28,15 +28,9 @@ - + - - org.apache.commons.codec. - org.apache.commons.logging. - org.apache.http. - com.google.gson. - diff --git a/jetty9-compat-base/src/test/java/com/google/apphosting/runtime/jetty9/SessionManagerIT.java b/jetty9-compat-base/src/test/java/com/google/apphosting/runtime/jetty9/SessionManagerIT.java index 5c92f7d..f06768f 100644 --- a/jetty9-compat-base/src/test/java/com/google/apphosting/runtime/jetty9/SessionManagerIT.java +++ b/jetty9-compat-base/src/test/java/com/google/apphosting/runtime/jetty9/SessionManagerIT.java @@ -54,16 +54,21 @@ import org.easymock.EasyMock; +import java.io.Closeable; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @@ -429,12 +434,17 @@ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundEx } } - public void testNonDeserializableSession() { - AppEngineSession session = createSession(); - session.setAttribute("key", new NonDeserializable(1)); - session.save(); + public void testNonDeserializableSession() throws Exception { + try (SuppressLogging logging = + new SuppressLogging( + SessionManager.class.getName(), + "com.google.appengine.tools.development.ApiProxyLocalImpl")) { + AppEngineSession session = createSession(); + session.setAttribute("key", new NonDeserializable(1)); + session.save(); - assertNull(retrieveSession(session)); + assertNull(retrieveSession(session)); + } } public void testSessionNotAvailableInMemcache() throws EntityNotFoundException { @@ -570,4 +580,26 @@ private HttpServletRequest makeMockRequest(boolean forSession) { return mockRequest; } + + class SuppressLogging implements Closeable { + List loggers = new ArrayList<>(); + List levels = new ArrayList<>(); + + SuppressLogging(String... names) { + for (String n : names) { + Logger logger = Logger.getLogger(n); + loggers.add(logger); + levels.add(logger.getLevel()); + logger.setLevel(Level.OFF); + } + } + + @Override + public void close() throws IOException { + Iterator level = levels.iterator(); + for (Logger logger : loggers) { + logger.setLevel(level.next()); + } + } + } } diff --git a/jetty9-compat-base/src/test/java/com/google/apphosting/vmruntime/jetty9/JettyRunner.java b/jetty9-compat-base/src/test/java/com/google/apphosting/vmruntime/jetty9/JettyRunner.java index 3f8541c..88fae47 100644 --- a/jetty9-compat-base/src/test/java/com/google/apphosting/vmruntime/jetty9/JettyRunner.java +++ b/jetty9-compat-base/src/test/java/com/google/apphosting/vmruntime/jetty9/JettyRunner.java @@ -43,9 +43,11 @@ import java.io.File; import java.io.IOException; import java.net.ServerSocket; +import java.util.Arrays; import java.util.Collections; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; class JettyRunner extends AbstractLifeCycle implements Runnable { @@ -55,16 +57,6 @@ class JettyRunner extends AbstractLifeCycle implements Runnable { private final String webapp; private String appengineWebXml; private final CountDownLatch started = new CountDownLatch(1); - private static final String[] preconfigurationClasses = { - org.eclipse.jetty.webapp.WebInfConfiguration.class.getCanonicalName(), - org.eclipse.jetty.webapp.WebXmlConfiguration.class.getCanonicalName(), - org.eclipse.jetty.webapp.MetaInfConfiguration.class.getCanonicalName(), - org.eclipse.jetty.webapp.FragmentConfiguration.class.getCanonicalName(), - org.eclipse.jetty.plus.webapp.EnvConfiguration.class.getCanonicalName(), - org.eclipse.jetty.plus.webapp.PlusConfiguration.class.getCanonicalName(), - // next one is way too slow for unit testing: - //org.eclipse.jetty.annotations.AnnotationConfiguration.class.getCanonicalName() - }; public JettyRunner() { this(-1); @@ -118,12 +110,15 @@ public void doStart() throws Exception { target = new File(project, "jetty9-compat-base/target"); } - File jettyBase = - new File( - System.getProperty("jetty.base", new File(target, "jetty-base").getAbsolutePath())); + String jettyBase = System.getProperty("jetty.base"); + if (jettyBase == null) { + jettyBase = new File(target, "jetty-base").getAbsolutePath(); + System.setProperty("jetty.base", jettyBase); + } + File jettyBaseFile = new File(jettyBase); Assert.assertTrue(target.isDirectory()); - Assert.assertTrue(jettyBase.isDirectory()); + Assert.assertTrue(jettyBaseFile.isDirectory()); logs = new File(target, "logs"); logs.delete(); logs.mkdirs(); @@ -167,8 +162,7 @@ public void doStart() throws Exception { httpConfig.setSendServerVersion(true); httpConfig.setSendDateHeader(false); httpConfig.setDelayDispatchUntilContent(false); - GoogleRequestCustomizer requestCustomizer = - new GoogleRequestCustomizer(port, 443); + GoogleRequestCustomizer requestCustomizer = new GoogleRequestCustomizer(port, 443); httpConfig.addCustomizer(requestCustomizer); // Setup Server as done by gae.xml @@ -192,9 +186,16 @@ public void doStart() throws Exception { // configuration from root.xml final VmRuntimeWebAppContext context = new VmRuntimeWebAppContext(); context.setContextPath("/"); - context.setConfigurationClasses(preconfigurationClasses); + + // remove annotations as they are too slow for testing + context.setConfigurationClasses( + Arrays.stream(context.getConfigurationClasses()) + .filter(n -> !n.contains("AnnotationConfiguration")) + .collect(Collectors.toList())); // Needed to initialize JSP! + context.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", + ".*/[^/]*taglibs.*\\.jar$"); context.addBean( new AbstractLifeCycle() { @Override @@ -224,7 +225,7 @@ public void doStart() throws Exception { context.setParentLoaderPriority(true); // true in tests for easier mocking // Hack to find the webdefault.xml - File webDefault = new File(jettyBase, "etc/webdefault.xml"); + File webDefault = new File(jettyBaseFile, "etc/webdefault.xml"); context.setDefaultsDescriptor(webDefault.getAbsolutePath()); contexts.addHandler(context); diff --git a/jetty9-compat-base/src/test/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeJettyKitchenSinkIT.java b/jetty9-compat-base/src/test/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeJettyKitchenSinkIT.java index 8d3b80c..34006e8 100644 --- a/jetty9-compat-base/src/test/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeJettyKitchenSinkIT.java +++ b/jetty9-compat-base/src/test/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeJettyKitchenSinkIT.java @@ -53,7 +53,6 @@ public void testJspNotCompiled() throws Exception { fetchUrl(createUrl(String.format("/hello_not_compiled.jsp?start=%d&end=%d", iter, end))); String iterationFormat = "

Iteration %d

"; for (String line : lines) { - System.out.println(line); if (!line.contains("Iteration")) { continue; } @@ -62,6 +61,27 @@ public void testJspNotCompiled() throws Exception { assertEquals(end + 1, iter); } + /** + * Test that non compiled jstl JSP can be served. + */ + public void testJstlJSP() throws Exception { + String[] lines = fetchUrl(createUrl("/jstl.jsp")); + int max = -1; + int count = 0; + for (String line : lines) { + line = line.trim(); + if (!line.isEmpty() && Character.isDigit(line.charAt(0))) { + int value = Integer.parseInt(line); + count++; + if (value > max) { + max = value; + } + } + } + assertEquals(10, count); + assertEquals(10, max); + } + /** * Tests that mapping a servlet to / works. */ diff --git a/pom.xml b/pom.xml index 02ae6a9..864b061 100644 --- a/pom.xml +++ b/pom.xml @@ -130,7 +130,13 @@ - + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + org.apache.maven.plugins maven-compiler-plugin @@ -157,6 +163,9 @@ org.apache.maven.plugins maven-surefire-plugin 2.18 + + false + @@ -243,6 +252,9 @@ org.apache.maven.plugins maven-failsafe-plugin 2.19.1 + + false + diff --git a/testwebapp/src/main/webapp/jstl.jsp b/testwebapp/src/main/webapp/jstl.jsp new file mode 100644 index 0000000..9fa7b57 --- /dev/null +++ b/testwebapp/src/main/webapp/jstl.jsp @@ -0,0 +1,15 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + +

JSTL Example

+
+

A trivial jstl example +


+ + +
+
+ +