/* * 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.appengine.repackaged.com.google.common.base.CharMatcher; import com.google.appengine.repackaged.com.google.common.collect.Lists; import com.google.appengine.repackaged.com.google.common.collect.Maps; import java.security.Permissions; import java.security.UnresolvedPermission; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.regex.Pattern; /** * Struct describing the config data that lives in WEB-INF/appengine-web.xml. * * Any additions to this class should also be made to the YAML * version in AppYaml.java. * */ public class AppEngineWebXml implements Cloneable { /** * Enumeration of supported scaling types. */ public static enum ScalingType { AUTOMATIC, MANUAL, BASIC } // System properties defined by the application in appengine-web.xml private final Map<String, String> systemProperties = Maps.newHashMap(); // Beta settings defined by the application in appengine-web.xml. // Using a linked hash map is not strictly needed but makes testing easier. private final Map<String, String> betaSettings = Maps.newLinkedHashMap(); private final HealthCheck healthCheck; private final Resources resources; private final Network network; // Environment variables defined by the application in appengine-web.xml private final Map<String, String> envVariables = Maps.newHashMap(); private final List<UserPermission> userPermissions = new ArrayList<UserPermission>(); public static final String WARMUP_SERVICE = "warmup"; public static final String URL_HANDLER_URLFETCH = "urlfetch"; public static final String URL_HANDLER_NATIVE = "native"; // Runtime ids. private static final String JAVA_7_RUNTIME_ID = "java7"; // Should accept java8* for multiple variations of Java8. private static final String JAVA_8_RUNTIME_ID = "java8"; // This was used for Java6, but now is used only for Managed VMs, // not for standard editiom. private static final String JAVA_RUNTIME_ID = "java"; private String appId; private String majorVersionId; private String module; // Used to be module, but now replaced by service. private String service; private String instanceClass; private final AutomaticScaling automaticScaling; private final ManualScaling manualScaling; private final BasicScaling basicScaling; private String runtime; private boolean sslEnabled = true; private boolean useSessions = false; private boolean asyncSessionPersistence = false; private String asyncSessionPersistenceQueueName; private final List<StaticFileInclude> staticFileIncludes; private final List<String> staticFileExcludes; private final List<String> resourceFileIncludes; private final List<String> resourceFileExcludes; private Pattern staticIncludePattern; private Pattern staticExcludePattern; private Pattern resourceIncludePattern; private Pattern resourceExcludePattern; private String publicRoot = ""; private String appRoot; private final Set<String> inboundServices; private boolean precompilationEnabled = true; private final List<AdminConsolePage> adminConsolePages = new ArrayList<AdminConsolePage>(); private final List<ErrorHandler> errorHandlers = new ArrayList<ErrorHandler>(); private ClassLoaderConfig classLoaderConfig; private String urlStreamHandlerType = null; // TODO(user): Set this to true at some future point. private boolean threadsafe = false; // Keeps track of whether or not there was a <threadsafe> element in the xml. // It would be easier to just make the threadsafe member variable a Boolean // but we can't change the public signature of this method. private boolean threadsafeValueProvided = false; // TODO(user): Revert once sequential auto id deprecation period expires. private String autoIdPolicy; private boolean codeLock = false; private boolean useVm = false; // Default env is standard (GAE V1) // TODO(user): Add a test emitting an error if "env" is present but it has an invalid value. private String env = "standard"; private ApiConfig apiConfig; private final List<String> apiEndpointIds; /** * Represent user's choice w.r.t the usage of Google's customized connector-j. */ public static enum UseGoogleConnectorJ { NOT_STATED_BY_USER, TRUE, FALSE, } // Identify if the user has explicitly stated if the application wishes to use Google's // customized connector-j. private UseGoogleConnectorJ useGoogleConnectorJ = UseGoogleConnectorJ.NOT_STATED_BY_USER; public AppEngineWebXml() { automaticScaling = new AutomaticScaling(); manualScaling = new ManualScaling(); basicScaling = new BasicScaling(); healthCheck = new HealthCheck(); resources = new Resources(); network = new Network(); staticFileIncludes = new ArrayList<StaticFileInclude>(); staticFileExcludes = new ArrayList<String>(); staticFileExcludes.add("WEB-INF/**"); staticFileExcludes.add("**.jsp"); resourceFileIncludes = new ArrayList<String>(); resourceFileExcludes = new ArrayList<String>(); inboundServices = new LinkedHashSet<String>(); apiEndpointIds = new ArrayList<String>(); } public AppEngineWebXml clone() { try { return (AppEngineWebXml) super.clone(); } catch (CloneNotSupportedException ce) { throw new RuntimeException("Could not clone AppEngineWebXml", ce); } } /** * @return An unmodifiable map whose entries correspond to the * system properties defined in appengine-web.xml. */ public Map<String, String> getSystemProperties() { return Collections.unmodifiableMap(systemProperties); } public void addSystemProperty(String key, String value) { systemProperties.put(key, value); } /** * @return An unmodifiable map whose entires correspond to the * vm settings defined in appengine-web.xml. */ public Map<String, String> getBetaSettings() { return Collections.unmodifiableMap(betaSettings); } public void addBetaSetting(String key, String value) { betaSettings.put(key, value); } public HealthCheck getHealthCheck() { return healthCheck; } public Resources getResources() { return resources; } public Network getNetwork() { return network; } /** * @return An unmodifiable map whose entires correspond to the * environment variables defined in appengine-web.xml. */ public Map<String, String> getEnvironmentVariables() { return Collections.unmodifiableMap(envVariables); } public void addEnvironmentVariable(String key, String value) { envVariables.put(key, value); } public String getAppId() { return appId; } public void setAppId(String appId) { this.appId = appId; } public String getMajorVersionId() { return majorVersionId; } public void setMajorVersionId(String majorVersionId) { this.majorVersionId = majorVersionId; } public String getRuntime() { if (runtime != null) { return runtime; } // The new env:flex means java, not java7: if (isFlexible()) { runtime = JAVA_RUNTIME_ID; } else { runtime = JAVA_7_RUNTIME_ID; } return runtime; } public void setRuntime(String runtime) { this.runtime = runtime; } public String getModule() { return module; } public String getService() { return service; } /** * Sets instanceClass (aka class in the xml/yaml files). Normalizes empty and null * inputs to null. */ public void setInstanceClass(String instanceClass) { this.instanceClass = toNullIfEmptyOrWhitespace(instanceClass); } public String getInstanceClass() { return instanceClass; } public AutomaticScaling getAutomaticScaling() { return automaticScaling; } public ManualScaling getManualScaling() { return manualScaling; } public BasicScaling getBasicScaling() { return basicScaling; } public ScalingType getScalingType() { if (!getBasicScaling().isEmpty()) { return ScalingType.BASIC; } else if (!getManualScaling().isEmpty()) { return ScalingType.MANUAL; } else { return ScalingType.AUTOMATIC; } } public void setModule(String module) { this.module = module; } public void setService(String service) { this.service = service; } public void setSslEnabled(boolean ssl) { sslEnabled = ssl; } public boolean getSslEnabled() { return sslEnabled; } public void setSessionsEnabled(boolean sessions) { useSessions = sessions; } public boolean getSessionsEnabled() { return useSessions; } public void setAsyncSessionPersistence(boolean asyncSessionPersistence) { this.asyncSessionPersistence = asyncSessionPersistence; } public boolean getAsyncSessionPersistence() { return asyncSessionPersistence; } public void setAsyncSessionPersistenceQueueName(String asyncSessionPersistenceQueueName) { this.asyncSessionPersistenceQueueName = asyncSessionPersistenceQueueName; } public String getAsyncSessionPersistenceQueueName() { return asyncSessionPersistenceQueueName; } public List<StaticFileInclude> getStaticFileIncludes() { return staticFileIncludes; } public List<String> getStaticFileExcludes() { return staticFileExcludes; } public StaticFileInclude includeStaticPattern(String pattern, String expiration) { staticIncludePattern = null; StaticFileInclude staticFileInclude = new StaticFileInclude(pattern, expiration); staticFileIncludes.add(staticFileInclude); return staticFileInclude; } public void excludeStaticPattern(String url) { staticExcludePattern = null; staticFileExcludes.add(url); } public List<String> getResourcePatterns() { return resourceFileIncludes; } public List<String> getResourceFileExcludes() { return resourceFileExcludes; } public void includeResourcePattern(String url) { resourceExcludePattern = null; resourceFileIncludes.add(url); } public void excludeResourcePattern(String url) { resourceIncludePattern = null; resourceFileExcludes.add(url); } public void addUserPermission(String className, String name, String actions) { if (className.startsWith("java.")) { throw new AppEngineConfigException( "Cannot specify user-permissions for " + "classes in java.* packages."); } userPermissions.add(new UserPermission(className, name, actions)); } public Permissions getUserPermissions() { Permissions permissions = new Permissions(); for (UserPermission permission : userPermissions) { permissions.add( new UnresolvedPermission( permission.getClassName(), permission.getName(), permission.getActions(), null)); } permissions.setReadOnly(); return permissions; } public void setPublicRoot(String root) { if (root.indexOf('*') != -1) { throw new AppEngineConfigException("public-root cannot contain wildcards"); } if (root.endsWith("/")) { root = root.substring(0, root.length() - 1); } if (root.length() > 0 && !root.startsWith("/")) { root = "/" + root; } staticIncludePattern = null; publicRoot = root; } public String getPublicRoot() { return publicRoot; } public void addInboundService(String service) { inboundServices.add(service); } public Set<String> getInboundServices() { return inboundServices; } public boolean getPrecompilationEnabled() { return precompilationEnabled; } public void setPrecompilationEnabled(boolean precompilationEnabled) { this.precompilationEnabled = precompilationEnabled; } public boolean getWarmupRequestsEnabled() { return inboundServices.contains(WARMUP_SERVICE); } public void setWarmupRequestsEnabled(boolean warmupRequestsEnabled) { if (warmupRequestsEnabled) { inboundServices.add(WARMUP_SERVICE); } else { inboundServices.remove(WARMUP_SERVICE); } } public List<AdminConsolePage> getAdminConsolePages() { return Collections.unmodifiableList(adminConsolePages); } public void addAdminConsolePage(AdminConsolePage page) { adminConsolePages.add(page); } public List<ErrorHandler> getErrorHandlers() { return Collections.unmodifiableList(errorHandlers); } public void addErrorHandler(ErrorHandler handler) { errorHandlers.add(handler); } public boolean getThreadsafe() { return threadsafe; } public boolean getThreadsafeValueProvided() { return threadsafeValueProvided; } public void setThreadsafe(boolean threadsafe) { this.threadsafe = threadsafe; this.threadsafeValueProvided = true; } public void setAutoIdPolicy(String policy) { autoIdPolicy = policy; } public String getAutoIdPolicy() { return autoIdPolicy; } public boolean getCodeLock() { return codeLock; } public void setCodeLock(boolean codeLock) { this.codeLock = codeLock; } public void setUseVm(boolean useVm) { this.useVm = useVm; } public boolean getUseVm() { return useVm; } public void setEnv(String env) { this.env = env; } public String getEnv() { return env; } public boolean isFlexible() { return ("flex".equalsIgnoreCase(env) || "2".equals(env) || "flexible".equalsIgnoreCase(env)); } public ApiConfig getApiConfig() { return apiConfig; } public void setApiConfig(ApiConfig config) { apiConfig = config; } public ClassLoaderConfig getClassLoaderConfig() { return classLoaderConfig; } public void setClassLoaderConfig(ClassLoaderConfig classLoaderConfig) { if (this.classLoaderConfig != null) { throw new AppEngineConfigException("class-loader-config may only be specified once."); } this.classLoaderConfig = classLoaderConfig; } public String getUrlStreamHandlerType() { return urlStreamHandlerType; } public void setUrlStreamHandlerType(String urlStreamHandlerType) { if (this.classLoaderConfig != null) { throw new AppEngineConfigException("url-stream-handler may only be specified once."); } if (!URL_HANDLER_URLFETCH.equals(urlStreamHandlerType) && !URL_HANDLER_NATIVE.equals(urlStreamHandlerType)) { throw new AppEngineConfigException( "url-stream-handler must be " + URL_HANDLER_URLFETCH + " or " + URL_HANDLER_NATIVE + " given " + urlStreamHandlerType); } this.urlStreamHandlerType = urlStreamHandlerType; } /** * Returns true if {@code url} matches one of the servlets or servlet * filters listed in this web.xml that has api-endpoint set to true. */ public boolean isApiEndpoint(String id) { return apiEndpointIds.contains(id); } public void addApiEndpoint(String id) { apiEndpointIds.add(id); } public void setUseGoogleConnectorJ(boolean useGoogleConnectorJ) { if (useGoogleConnectorJ) { this.useGoogleConnectorJ = UseGoogleConnectorJ.TRUE; } else { this.useGoogleConnectorJ = UseGoogleConnectorJ.FALSE; } } public UseGoogleConnectorJ getUseGoogleConnectorJ() { return useGoogleConnectorJ; } @Override public String toString() { return "AppEngineWebXml{" + "systemProperties=" + systemProperties + ", envVariables=" + envVariables + ", userPermissions=" + userPermissions + ", appId='" + appId + '\'' + ", majorVersionId='" + majorVersionId + '\'' + ", runtime='" + runtime + '\'' + ", service='" + service + '\'' + ", instanceClass='" + instanceClass + '\'' + ", automaticScaling=" + automaticScaling + ", manualScaling=" + manualScaling + ", basicScaling=" + basicScaling + ", healthCheck=" + healthCheck + ", resources=" + resources + ", network=" + network + ", sslEnabled=" + sslEnabled + ", useSessions=" + useSessions + ", asyncSessionPersistence=" + asyncSessionPersistence + ", asyncSessionPersistenceQueueName='" + asyncSessionPersistenceQueueName + '\'' + ", staticFileIncludes=" + staticFileIncludes + ", staticFileExcludes=" + staticFileExcludes + ", resourceFileIncludes=" + resourceFileIncludes + ", resourceFileExcludes=" + resourceFileExcludes + ", staticIncludePattern=" + staticIncludePattern + ", staticExcludePattern=" + staticExcludePattern + ", resourceIncludePattern=" + resourceIncludePattern + ", resourceExcludePattern=" + resourceExcludePattern + ", publicRoot='" + publicRoot + '\'' + ", appRoot='" + appRoot + '\'' + ", inboundServices=" + inboundServices + ", precompilationEnabled=" + precompilationEnabled + ", adminConsolePages=" + adminConsolePages + ", errorHandlers=" + errorHandlers + ", threadsafe=" + threadsafe + ", threadsafeValueProvided=" + threadsafeValueProvided + ", autoIdPolicy=" + autoIdPolicy + ", codeLock=" + codeLock + ", apiConfig=" + apiConfig + ", apiEndpointIds=" + apiEndpointIds + ", classLoaderConfig=" + classLoaderConfig + ", urlStreamHandlerType=" + (urlStreamHandlerType == null ? URL_HANDLER_URLFETCH : urlStreamHandlerType) + ", useGoogleConnectorJ=" + useGoogleConnectorJ + '}'; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } AppEngineWebXml that = (AppEngineWebXml) o; if (asyncSessionPersistence != that.asyncSessionPersistence) { return false; } if (precompilationEnabled != that.precompilationEnabled) { return false; } if (sslEnabled != that.sslEnabled) { return false; } if (threadsafe != that.threadsafe) { return false; } if (threadsafeValueProvided != that.threadsafeValueProvided) { return false; } if (autoIdPolicy != null ? !autoIdPolicy.equals(that.autoIdPolicy) : that.autoIdPolicy != null) { return false; } if (codeLock != that.codeLock) { return false; } if (useSessions != that.useSessions) { return false; } if (adminConsolePages != null ? !adminConsolePages.equals(that.adminConsolePages) : that.adminConsolePages != null) { return false; } if (appId != null ? !appId.equals(that.appId) : that.appId != null) { return false; } if (majorVersionId != null ? !majorVersionId.equals(that.majorVersionId) : that.majorVersionId != null) { return false; } if (service != null ? !service.equals(that.service) : that.service != null) { return false; } if (instanceClass != null ? !instanceClass.equals(that.instanceClass) : that.instanceClass != null) { return false; } if (!automaticScaling.equals(that.automaticScaling)) { return false; } if (!manualScaling.equals(that.manualScaling)) { return false; } if (!basicScaling.equals(that.basicScaling)) { return false; } if (appRoot != null ? !appRoot.equals(that.appRoot) : that.appRoot != null) { return false; } if (asyncSessionPersistenceQueueName != null ? !asyncSessionPersistenceQueueName.equals(that.asyncSessionPersistenceQueueName) : that.asyncSessionPersistenceQueueName != null) { return false; } if (envVariables != null ? !envVariables.equals(that.envVariables) : that.envVariables != null) { return false; } if (errorHandlers != null ? !errorHandlers.equals(that.errorHandlers) : that.errorHandlers != null) { return false; } if (inboundServices != null ? !inboundServices.equals(that.inboundServices) : that.inboundServices != null) { return false; } if (majorVersionId != null ? !majorVersionId.equals(that.majorVersionId) : that.majorVersionId != null) { return false; } if (runtime != null ? !runtime.equals(that.runtime) : that.runtime != null) { return false; } if (publicRoot != null ? !publicRoot.equals(that.publicRoot) : that.publicRoot != null) { return false; } if (resourceExcludePattern != null ? !resourceExcludePattern.equals(that.resourceExcludePattern) : that.resourceExcludePattern != null) { return false; } if (resourceFileExcludes != null ? !resourceFileExcludes.equals(that.resourceFileExcludes) : that.resourceFileExcludes != null) { return false; } if (resourceFileIncludes != null ? !resourceFileIncludes.equals(that.resourceFileIncludes) : that.resourceFileIncludes != null) { return false; } if (resourceIncludePattern != null ? !resourceIncludePattern.equals(that.resourceIncludePattern) : that.resourceIncludePattern != null) { return false; } if (staticExcludePattern != null ? !staticExcludePattern.equals(that.staticExcludePattern) : that.staticExcludePattern != null) { return false; } if (staticFileExcludes != null ? !staticFileExcludes.equals(that.staticFileExcludes) : that.staticFileExcludes != null) { return false; } if (staticFileIncludes != null ? !staticFileIncludes.equals(that.staticFileIncludes) : that.staticFileIncludes != null) { return false; } if (staticIncludePattern != null ? !staticIncludePattern.equals(that.staticIncludePattern) : that.staticIncludePattern != null) { return false; } if (systemProperties != null ? !systemProperties.equals(that.systemProperties) : that.systemProperties != null) { return false; } if (betaSettings != null ? !betaSettings.equals(that.betaSettings) : that.betaSettings != null) { return false; } if (healthCheck != null ? !healthCheck.equals(that.healthCheck) : that.healthCheck != null) { return false; } if (resources != null ? !resources.equals(that.resources) : that.resources != null) { return false; } if (network != null ? !network.equals(that.network) : that.network != null) { return false; } if (userPermissions != null ? !userPermissions.equals(that.userPermissions) : that.userPermissions != null) { return false; } if (apiConfig != null ? !apiConfig.equals(that.apiConfig) : that.apiConfig != null) { return false; } if (apiEndpointIds != null ? !apiEndpointIds.equals(that.apiEndpointIds) : that.apiEndpointIds != null) { return false; } if (classLoaderConfig != null ? !classLoaderConfig.equals(that.classLoaderConfig) : that.classLoaderConfig != null) { return false; } if (urlStreamHandlerType != null ? !urlStreamHandlerType.equals(that.urlStreamHandlerType) : that.urlStreamHandlerType != null) { return false; } if (useGoogleConnectorJ != that.useGoogleConnectorJ) { return false; } return true; } @Override public int hashCode() { int result = systemProperties != null ? systemProperties.hashCode() : 0; result = 31 * result + (envVariables != null ? envVariables.hashCode() : 0); result = 31 * result + (userPermissions != null ? userPermissions.hashCode() : 0); result = 31 * result + (appId != null ? appId.hashCode() : 0); result = 31 * result + (majorVersionId != null ? majorVersionId.hashCode() : 0); result = 31 * result + (runtime != null ? runtime.hashCode() : 0); result = 31 * result + (service != null ? service.hashCode() : 0); result = 31 * result + (instanceClass != null ? instanceClass.hashCode() : 0); result = 31 * result + automaticScaling.hashCode(); result = 31 * result + manualScaling.hashCode(); result = 31 * result + basicScaling.hashCode(); result = 31 * result + (sslEnabled ? 1 : 0); result = 31 * result + (useSessions ? 1 : 0); result = 31 * result + (asyncSessionPersistence ? 1 : 0); result = 31 * result + (asyncSessionPersistenceQueueName != null ? asyncSessionPersistenceQueueName.hashCode() : 0); result = 31 * result + (staticFileIncludes != null ? staticFileIncludes.hashCode() : 0); result = 31 * result + (staticFileExcludes != null ? staticFileExcludes.hashCode() : 0); result = 31 * result + (resourceFileIncludes != null ? resourceFileIncludes.hashCode() : 0); result = 31 * result + (resourceFileExcludes != null ? resourceFileExcludes.hashCode() : 0); result = 31 * result + (staticIncludePattern != null ? staticIncludePattern.hashCode() : 0); result = 31 * result + (staticExcludePattern != null ? staticExcludePattern.hashCode() : 0); result = 31 * result + (resourceIncludePattern != null ? resourceIncludePattern.hashCode() : 0); result = 31 * result + (resourceExcludePattern != null ? resourceExcludePattern.hashCode() : 0); result = 31 * result + (publicRoot != null ? publicRoot.hashCode() : 0); result = 31 * result + (appRoot != null ? appRoot.hashCode() : 0); result = 31 * result + (inboundServices != null ? inboundServices.hashCode() : 0); result = 31 * result + (precompilationEnabled ? 1 : 0); result = 31 * result + (adminConsolePages != null ? adminConsolePages.hashCode() : 0); result = 31 * result + (errorHandlers != null ? errorHandlers.hashCode() : 0); result = 31 * result + (threadsafe ? 1 : 0); result = 31 * result + (autoIdPolicy != null ? autoIdPolicy.hashCode() : 0); result = 31 * result + (threadsafeValueProvided ? 1 : 0); result = 31 * result + (codeLock ? 1 : 0); result = 31 * result + (apiConfig != null ? apiConfig.hashCode() : 0); result = 31 * result + (apiEndpointIds != null ? apiEndpointIds.hashCode() : 0); result = 31 * result + (classLoaderConfig != null ? classLoaderConfig.hashCode() : 0); result = 31 * result + (urlStreamHandlerType != null ? urlStreamHandlerType.hashCode() : 0); result = 31 * result + (useGoogleConnectorJ.hashCode()); result = 31 * result + (betaSettings != null ? betaSettings.hashCode() : 0); result = 31 * result + (healthCheck != null ? healthCheck.hashCode() : 0); result = 31 * result + (resources != null ? resources.hashCode() : 0); result = 31 * result + (network != null ? network.hashCode() : 0); return result; } public boolean includesResource(String path) { if (resourceIncludePattern == null) { if (resourceFileIncludes.size() == 0) { // if the user doesn't give any includes, we want everything resourceIncludePattern = Pattern.compile(".*"); } else { resourceIncludePattern = Pattern.compile(makeRegexp(resourceFileIncludes)); } } if (resourceExcludePattern == null && resourceFileExcludes.size() > 0) { resourceExcludePattern = Pattern.compile(makeRegexp(resourceFileExcludes)); } else { // if there are no resourceFileExcludes, let the pattern stay NULL. } return includes(path, resourceIncludePattern, resourceExcludePattern); } public boolean includesStatic(String path) { if (staticIncludePattern == null) { if (staticFileIncludes.size() == 0) { // if the user doesn't give any includes, we want everything under // publicRoot String staticRoot; if (publicRoot.length() > 0) { staticRoot = publicRoot + "/**"; } else { staticRoot = "**"; } staticIncludePattern = Pattern.compile(makeRegexp(Collections.singletonList(staticRoot))); } else { List<String> patterns = new ArrayList<String>(); for (StaticFileInclude include : staticFileIncludes) { patterns.add(include.getPattern()); } staticIncludePattern = Pattern.compile(makeRegexp(patterns)); } } if (staticExcludePattern == null && staticFileExcludes.size() > 0) { staticExcludePattern = Pattern.compile(makeRegexp(staticFileExcludes)); } else { // if there are no staticFileExcludes, let the pattern stay NULL. } return includes(path, staticIncludePattern, staticExcludePattern); } /** * Tests whether {@code path} is covered by the pattern {@code includes} * while not being blocked by matching {@code excludes}. * * @param path a URL to test * @param includes a non-{@code null} pattern for included URLs * @param excludes a pattern for exclusion, or {@code null} to not exclude * anything from the {@code includes} set. */ public boolean includes(String path, Pattern includes, Pattern excludes) { assert (includes != null); if (!includes.matcher(path).matches()) { return false; } if (excludes != null && excludes.matcher(path).matches()) { return false; } return true; } public String makeRegexp(List<String> patterns) { StringBuilder builder = new StringBuilder(); boolean first = true; for (String item : patterns) { if (first) { first = false; } else { builder.append('|'); } // Trim any leading slashes from item. while (item.charAt(0) == '/') { item = item.substring(1); } builder.append('('); if (appRoot != null) { builder.append(makeFileRegex(appRoot)); } builder.append("/"); builder.append(makeFileRegex(item)); builder.append(')'); } return builder.toString(); } /** * Helper method to translate from appengine-web.xml "file globs" to * proper regular expressions as used in app.yaml. * * @param fileGlob the glob to translate * @return the regular expression string matching the input {@code file} pattern. */ static String makeFileRegex(String fileGlob) { // escape metacharacters, and replace '*' with regexp '[^/]*' and '**' with '.*' fileGlob = fileGlob.replaceAll("([^A-Za-z0-9\\-_/])", "\\\\$1"); fileGlob = fileGlob.replaceAll("\\\\\\*\\\\\\*", ".*"); fileGlob = fileGlob.replaceAll("\\\\\\*", "[^/]*"); return fileGlob; } /** * Sets the application root directory, as a prefix for the regexps in * {@link #includeResourcePattern(String)} and friends. This is needed * because we want to match complete filenames relative to root. */ public void setSourcePrefix(String appRoot) { this.appRoot = appRoot; // Invalidate the pattern cache because the patterns // were generated with the previous root. this.resourceIncludePattern = null; this.resourceExcludePattern = null; this.staticIncludePattern = null; this.staticExcludePattern = null; } public String getSourcePrefix() { return this.appRoot; } private static String toNullIfEmptyOrWhitespace(String string) { if (string == null || CharMatcher.whitespace().matchesAllOf(string)) { return null; } return string; } /** * Represents a {@link java.security.Permission} that needs to be * granted to user code. */ private static class UserPermission { private final String className; private final String name; private final String actions; private boolean hasHashCode = false; private int hashCode; public UserPermission(String className, String name, String actions) { this.className = className; this.name = name; this.actions = actions; } public String getClassName() { return className; } public String getName() { return name; } public String getActions() { return actions; } @Override public int hashCode() { if (hasHashCode) { return hashCode; } int hash = className.hashCode(); hash = 31 * hash + name.hashCode(); if (actions != null) { hash = 31 * hash + actions.hashCode(); } hashCode = hash; hasHashCode = true; return hashCode; } @Override public boolean equals(Object obj) { if (obj instanceof UserPermission) { UserPermission perm = (UserPermission) obj; if (className.equals(perm.className) && name.equals(perm.name)) { if (actions == null ? perm.actions == null : actions.equals(perm.actions)) { return true; } } } return false; } } /** * Represents an <include> element within the * <static-files> element. Currently this includes both a * pattern and an optional expiration time specification. */ public static class StaticFileInclude { private final String pattern; private final String expiration; private final Map<String, String> httpHeaders; public StaticFileInclude(String pattern, String expiration) { this.pattern = pattern; this.expiration = expiration; this.httpHeaders = new LinkedHashMap<>(); } public String getPattern() { return pattern; } public Pattern getRegularExpression() { return Pattern.compile(makeFileRegex(pattern)); } public String getExpiration() { return expiration; } public Map<String, String> getHttpHeaders() { return httpHeaders; } @Override public int hashCode() { return Objects.hash(pattern, expiration, httpHeaders); } @Override public boolean equals(Object obj) { if (!(obj instanceof StaticFileInclude)) { return false; } StaticFileInclude other = (StaticFileInclude) obj; // If patterns differ, return false. if (pattern != null) { if (!pattern.equals(other.pattern)) { return false; } } else { // pattern == null if (other.pattern != null) { return false; } // else, both patterns null; thus, they do not differ. } // If expirations differ, return false. if (expiration != null) { if (!expiration.equals(other.expiration)) { return false; } } else { // expiration == null if (other.expiration != null) { return false; } // else, both expirations null; thus, they do not differ. } // If httpHeaders differ, return false. if (httpHeaders != null) { if (!httpHeaders.equals(other.httpHeaders)) { return false; } } else { // httpHeaders == null if (other.httpHeaders != null) { return false; } // else, both httpHeaders null; thus, they do not differ. } // All attributes are equal; thus, this equals other. return true; } } public static class AdminConsolePage { private final String name; private final String url; public AdminConsolePage(String name, String url) { this.name = name; this.url = url; } public String getName() { return name; } public String getUrl() { return url; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((url == null) ? 0 : url.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } AdminConsolePage other = (AdminConsolePage) obj; if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } if (url == null) { if (other.url != null) { return false; } } else if (!url.equals(other.url)) { return false; } return true; } } /** * Represents an <error-handler> element. Currently this includes both * a file name and an optional error code. */ public static class ErrorHandler { private final String file; private final String errorCode; public ErrorHandler(String file, String errorCode) { this.file = file; this.errorCode = errorCode; } public String getFile() { return file; } public String getErrorCode() { return errorCode; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((file == null) ? 0 : file.hashCode()); result = prime * result + ((errorCode == null) ? 0 : errorCode.hashCode()); return result; } @Override public boolean equals(Object obj) { if (!(obj instanceof ErrorHandler)) { return false; } ErrorHandler handler = (ErrorHandler) obj; if (!file.equals(handler.file)) { return false; } if (errorCode == null) { if (handler.errorCode != null) { return false; } } else { if (!errorCode.equals(handler.errorCode)) { return false; } } return true; } } /** * Represents an <api-config> element. This is a singleton specifying * url-pattern and servlet-class for the api config server. */ public static class ApiConfig { private final String servletClass; private final String url; public ApiConfig(String servletClass, String url) { this.servletClass = servletClass; this.url = url; } public String getservletClass() { return servletClass; } public String getUrl() { return url; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((servletClass == null) ? 0 : servletClass.hashCode()); result = prime * result + ((url == null) ? 0 : url.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } ApiConfig other = (ApiConfig) obj; if (servletClass == null) { if (other.servletClass != null) { return false; } } else if (!servletClass.equals(other.servletClass)) { return false; } if (url == null) { if (other.url != null) { return false; } } else if (!url.equals(other.url)) { return false; } return true; } @Override public String toString() { return "ApiConfig{servletClass=\"" + servletClass + "\", url=\"" + url + "\"}"; } } /** * Holder for automatic settings. */ public static class AutomaticScaling { /* * AutomaticScaling with no fields set. * * Keep this private because AutomaticScaling is mutable. */ private static final AutomaticScaling EMPTY_SETTINGS = new AutomaticScaling(); public static final String AUTOMATIC = "automatic"; private String minPendingLatency; private String maxPendingLatency; private String minIdleInstances; private String maxIdleInstances; private String maxConcurrentRequests; private Integer minNumInstances; private Integer maxNumInstances; private Integer coolDownPeriodSec; private CpuUtilization cpuUtilization; private Integer targetNetworkSentBytesPerSec; private Integer targetNetworkSentPacketsPerSec; private Integer targetNetworkReceivedBytesPerSec; private Integer targetNetworkReceivedPacketsPerSec; private Integer targetDiskWriteBytesPerSec; private Integer targetDiskWriteOpsPerSec; private Integer targetDiskReadBytesPerSec; private Integer targetDiskReadOpsPerSec; private Integer targetRequestCountPerSec; private Integer targetConcurrentRequests; public String getMinPendingLatency() { return minPendingLatency; } /** * Sets minPendingLatency. Normalizes empty and null inputs to null. */ public void setMinPendingLatency(String minPendingLatency) { this.minPendingLatency = toNullIfEmptyOrWhitespace(minPendingLatency); } public String getMaxPendingLatency() { return maxPendingLatency; } /** * Sets maxPendingLatency. Normalizes empty and null inputs to null. */ public void setMaxPendingLatency(String maxPendingLatency) { this.maxPendingLatency = toNullIfEmptyOrWhitespace(maxPendingLatency); } public String getMinIdleInstances() { return minIdleInstances; } /** * Sets minIdleInstances. Normalizes empty and null inputs to null. */ public void setMinIdleInstances(String minIdleInstances) { this.minIdleInstances = toNullIfEmptyOrWhitespace(minIdleInstances); } public String getMaxIdleInstances() { return maxIdleInstances; } /** * Sets maxIdleInstances. Normalizes empty and null inputs to null. */ public void setMaxIdleInstances(String maxIdleInstances) { this.maxIdleInstances = toNullIfEmptyOrWhitespace(maxIdleInstances); } public boolean isEmpty() { return this.equals(EMPTY_SETTINGS); } public String getMaxConcurrentRequests() { return maxConcurrentRequests; } /** * Sets maxConcurrentRequests. Normalizes empty and null inputs to null. */ public void setMaxConcurrentRequests(String maxConcurrentRequests) { this.maxConcurrentRequests = toNullIfEmptyOrWhitespace(maxConcurrentRequests); } public Integer getMinNumInstances() { return minNumInstances; } public void setMinNumInstances(Integer minNumInstances) { this.minNumInstances = minNumInstances; } public Integer getMaxNumInstances() { return maxNumInstances; } public void setMaxNumInstances(Integer maxNumInstances) { this.maxNumInstances = maxNumInstances; } public Integer getCoolDownPeriodSec() { return coolDownPeriodSec; } public void setCoolDownPeriodSec(Integer coolDownPeriodSec) { this.coolDownPeriodSec = coolDownPeriodSec; } public CpuUtilization getCpuUtilization() { return cpuUtilization; } public void setCpuUtilization(CpuUtilization cpuUtilization) { this.cpuUtilization = cpuUtilization; } public Integer getTargetNetworkSentBytesPerSec() { return targetNetworkSentBytesPerSec; } public void setTargetNetworkSentBytesPerSec(Integer targetNetworkSentBytesPerSec) { this.targetNetworkSentBytesPerSec = targetNetworkSentBytesPerSec; } public Integer getTargetNetworkSentPacketsPerSec() { return targetNetworkSentPacketsPerSec; } public void setTargetNetworkSentPacketsPerSec(Integer targetNetworkSentPacketsPerSec) { this.targetNetworkSentPacketsPerSec = targetNetworkSentPacketsPerSec; } public Integer getTargetNetworkReceivedBytesPerSec() { return targetNetworkReceivedBytesPerSec; } public void setTargetNetworkReceivedBytesPerSec(Integer targetNetworkReceivedBytesPerSec) { this.targetNetworkReceivedBytesPerSec = targetNetworkReceivedBytesPerSec; } public Integer getTargetNetworkReceivedPacketsPerSec() { return targetNetworkReceivedPacketsPerSec; } public void setTargetNetworkReceivedPacketsPerSec(Integer targetNetworkReceivedPacketsPerSec) { this.targetNetworkReceivedPacketsPerSec = targetNetworkReceivedPacketsPerSec; } public Integer getTargetDiskWriteBytesPerSec() { return targetDiskWriteBytesPerSec; } public void setTargetDiskWriteBytesPerSec(Integer targetDiskWriteBytesPerSec) { this.targetDiskWriteBytesPerSec = targetDiskWriteBytesPerSec; } public Integer getTargetDiskWriteOpsPerSec() { return targetDiskWriteOpsPerSec; } public void setTargetDiskWriteOpsPerSec(Integer targetDiskWriteOpsPerSec) { this.targetDiskWriteOpsPerSec = targetDiskWriteOpsPerSec; } public Integer getTargetDiskReadBytesPerSec() { return targetDiskReadBytesPerSec; } public void setTargetDiskReadBytesPerSec(Integer targetDiskReadBytesPerSec) { this.targetDiskReadBytesPerSec = targetDiskReadBytesPerSec; } public Integer getTargetDiskReadOpsPerSec() { return targetDiskReadOpsPerSec; } public void setTargetDiskReadOpsPerSec(Integer targetDiskReadOpsPerSec) { this.targetDiskReadOpsPerSec = targetDiskReadOpsPerSec; } public Integer getTargetRequestCountPerSec() { return targetRequestCountPerSec; } public void setTargetRequestCountPerSec(Integer targetRequestCountPerSec) { this.targetRequestCountPerSec = targetRequestCountPerSec; } public Integer getTargetConcurrentRequests() { return targetConcurrentRequests; } public void setTargetConcurrentRequests(Integer targetConcurrentRequests) { this.targetConcurrentRequests = targetConcurrentRequests; } @Override public int hashCode() { return Objects.hash( maxPendingLatency, minPendingLatency, maxIdleInstances, minIdleInstances, maxConcurrentRequests, minNumInstances, maxNumInstances, coolDownPeriodSec, cpuUtilization, targetNetworkSentBytesPerSec, targetNetworkSentPacketsPerSec, targetNetworkReceivedBytesPerSec, targetNetworkReceivedPacketsPerSec, targetDiskWriteBytesPerSec, targetDiskWriteOpsPerSec, targetDiskReadBytesPerSec, targetDiskReadOpsPerSec, targetRequestCountPerSec, targetConcurrentRequests); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } AutomaticScaling other = (AutomaticScaling) obj; return Objects.equals(maxPendingLatency, other.maxPendingLatency) && Objects.equals(minPendingLatency, other.minPendingLatency) && Objects.equals(maxIdleInstances, other.maxIdleInstances) && Objects.equals(minIdleInstances, other.minIdleInstances) && Objects.equals(maxConcurrentRequests, other.maxConcurrentRequests) && Objects.equals(minNumInstances, other.minNumInstances) && Objects.equals(maxNumInstances, other.maxNumInstances) && Objects.equals(coolDownPeriodSec, other.coolDownPeriodSec) && Objects.equals(cpuUtilization, other.cpuUtilization) && Objects.equals(targetNetworkSentBytesPerSec, other.targetNetworkSentBytesPerSec) && Objects.equals(targetNetworkSentPacketsPerSec, other.targetNetworkSentPacketsPerSec) && Objects.equals( targetNetworkReceivedBytesPerSec, other.targetNetworkReceivedBytesPerSec) && Objects.equals( targetNetworkReceivedPacketsPerSec, other.targetNetworkReceivedPacketsPerSec) && Objects.equals(targetDiskWriteBytesPerSec, other.targetDiskWriteBytesPerSec) && Objects.equals(targetDiskWriteOpsPerSec, other.targetDiskWriteOpsPerSec) && Objects.equals(targetDiskReadBytesPerSec, other.targetDiskReadBytesPerSec) && Objects.equals(targetDiskReadOpsPerSec, other.targetDiskReadOpsPerSec) && Objects.equals(targetRequestCountPerSec, other.targetRequestCountPerSec) && Objects.equals(targetConcurrentRequests, other.targetConcurrentRequests); } @Override public String toString() { return "AutomaticScaling [minPendingLatency=" + minPendingLatency + ", maxPendingLatency=" + maxPendingLatency + ", minIdleInstances=" + minIdleInstances + ", maxIdleInstances=" + maxIdleInstances + ", maxConcurrentRequests=" + maxConcurrentRequests + ", minNumInstances=" + minNumInstances + ", maxNumInstances=" + maxNumInstances + ", coolDownPeriodSec=" + coolDownPeriodSec + ", cpuUtilization=" + cpuUtilization + ", targetNetworkSentBytesPerSec=" + targetNetworkSentBytesPerSec + ", targetNetworkSentPacketsPerSec=" + targetNetworkSentPacketsPerSec + ", targetNetworkReceivedBytesPerSec=" + targetNetworkReceivedBytesPerSec + ", targetNetworkReceivedPacketsPerSec=" + targetNetworkReceivedPacketsPerSec + ", targetDiskWriteBytesPerSec=" + targetDiskWriteBytesPerSec + ", targetDiskWriteOpsPerSec=" + targetDiskWriteOpsPerSec + ", targetDiskReadBytesPerSec=" + targetDiskReadBytesPerSec + ", targetDiskReadOpsPerSec=" + targetDiskReadOpsPerSec + ", targetRequestCountPerSec=" + targetRequestCountPerSec + ", targetConcurrentRequests=" + targetConcurrentRequests + "]"; } } /** * Holder for CPU utilization. */ public static class CpuUtilization { private static final CpuUtilization EMPTY_SETTINGS = new CpuUtilization(); // The target of CPU utilization. private Double targetUtilization; // The number of seconds used to aggregate CPU usage. private Integer aggregationWindowLengthSec; public Double getTargetUtilization() { return targetUtilization; } public void setTargetUtilization(Double targetUtilization) { this.targetUtilization = targetUtilization; } public Integer getAggregationWindowLengthSec() { return aggregationWindowLengthSec; } public void setAggregationWindowLengthSec(Integer aggregationWindowLengthSec) { this.aggregationWindowLengthSec = aggregationWindowLengthSec; } public boolean isEmpty() { return this.equals(EMPTY_SETTINGS); } @Override public int hashCode() { return Objects.hash(targetUtilization, aggregationWindowLengthSec); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } CpuUtilization other = (CpuUtilization) obj; return Objects.equals(targetUtilization, other.targetUtilization) && Objects.equals(aggregationWindowLengthSec, other.aggregationWindowLengthSec); } @Override public String toString() { return "CpuUtilization [targetUtilization=" + targetUtilization + ", aggregationWindowLengthSec=" + aggregationWindowLengthSec + "]"; } } /** * Holder for health check. */ public static class HealthCheck { /* * HealthCheck with no fields set. * * Keep this private because HealthCheck is mutable. */ private static final HealthCheck EMPTY_SETTINGS = new HealthCheck(); // Health check is enabled by default. private boolean enableHealthCheck = true; private Integer checkIntervalSec; private Integer timeoutSec; private Integer unhealthyThreshold; private Integer healthyThreshold; private Integer restartThreshold; private String host; public boolean getEnableHealthCheck() { return enableHealthCheck; } /** * Sets enableHealthCheck. */ public void setEnableHealthCheck(boolean enableHealthCheck) { this.enableHealthCheck = enableHealthCheck; } public Integer getCheckIntervalSec() { return checkIntervalSec; } /** * Sets checkIntervalSec. */ public void setCheckIntervalSec(Integer checkIntervalSec) { this.checkIntervalSec = checkIntervalSec; } public Integer getTimeoutSec() { return timeoutSec; } /** * Sets timeoutSec. */ public void setTimeoutSec(Integer timeoutSec) { this.timeoutSec = timeoutSec; } public Integer getUnhealthyThreshold() { return unhealthyThreshold; } /** * Sets unhealthyThreshold. */ public void setUnhealthyThreshold(Integer unhealthyThreshold) { this.unhealthyThreshold = unhealthyThreshold; } public Integer getHealthyThreshold() { return healthyThreshold; } /** * Sets healthyThreshold. */ public void setHealthyThreshold(Integer healthyThreshold) { this.healthyThreshold = healthyThreshold; } public Integer getRestartThreshold() { return restartThreshold; } /** * Sets restartThreshold. */ public void setRestartThreshold(Integer restartThreshold) { this.restartThreshold = restartThreshold; } public String getHost() { return host; } /** * Sets host. Normalizes empty and null inputs to null. */ public void setHost(String host) { this.host = toNullIfEmptyOrWhitespace(host); } public boolean isEmpty() { return this.equals(EMPTY_SETTINGS); } @Override public int hashCode() { return Objects.hash( enableHealthCheck, checkIntervalSec, timeoutSec, unhealthyThreshold, healthyThreshold, restartThreshold, host); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } HealthCheck other = (HealthCheck) obj; return Objects.equals(enableHealthCheck, other.enableHealthCheck) && Objects.equals(checkIntervalSec, other.checkIntervalSec) && Objects.equals(timeoutSec, other.timeoutSec) && Objects.equals(unhealthyThreshold, other.unhealthyThreshold) && Objects.equals(healthyThreshold, other.healthyThreshold) && Objects.equals(restartThreshold, other.restartThreshold) && Objects.equals(host, other.host); } @Override public String toString() { return "HealthCheck [enableHealthCheck=" + enableHealthCheck + ", checkIntervalSec=" + checkIntervalSec + ", timeoutSec=" + timeoutSec + ", unhealthyThreshold=" + unhealthyThreshold + ", healthyThreshold=" + healthyThreshold + ", restartThreshold=" + restartThreshold + ", host=" + host + "]"; } } /** * Holder for Resources */ public static class Resources { /* * Resources with no fields set. * * Keep this private because Resources is mutable. */ private static final Resources EMPTY_SETTINGS = new Resources(); private double cpu; public double getCpu() { return cpu; } public void setCpu(double cpu) { this.cpu = cpu; } private double memoryGb; public double getMemoryGb() { return memoryGb; } public void setMemoryGb(double memoryGb) { this.memoryGb = memoryGb; } private int diskSizeGb; public int getDiskSizeGb() { return diskSizeGb; } public void setDiskSizeGb(int diskSizeGb) { this.diskSizeGb = diskSizeGb; } public boolean isEmpty() { return this.equals(EMPTY_SETTINGS); } @Override public int hashCode() { return Objects.hash(cpu, memoryGb, diskSizeGb); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Resources other = (Resources) obj; return Objects.equals(cpu, other.cpu) && Objects.equals(memoryGb, other.memoryGb) && Objects.equals(diskSizeGb, other.diskSizeGb); } @Override public String toString() { return "Resources [" + "cpu=" + cpu + ", memoryGb=" + memoryGb + ", diskSizeGb=" + diskSizeGb + "]"; } } /** * Holder for network. */ public static class Network { /* * Network with no fields set. * * Keep this private because Network is mutable. */ private static final Network EMPTY_SETTINGS = new Network(); private String instanceTag; public String getInstanceTag() { return instanceTag; } public void setInstanceTag(String instanceTag) { this.instanceTag = instanceTag; } private final List<String> forwardedPorts = Lists.newArrayList(); public List<String> getForwardedPorts() { return Collections.unmodifiableList(forwardedPorts); } public void addForwardedPort(String forwardedPort) { forwardedPorts.add(forwardedPort); } private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isEmpty() { return this.equals(EMPTY_SETTINGS); } @Override public int hashCode() { return Objects.hash(forwardedPorts, instanceTag); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Network other = (Network) obj; return Objects.equals(forwardedPorts, other.forwardedPorts) && Objects.equals(instanceTag, other.instanceTag); } @Override public String toString() { return "Network [forwardedPorts=" + forwardedPorts + ", instanceTag=" + instanceTag + "]"; } } /** * Holder for manual settings. */ public static class ManualScaling { /* * ManualScaling with no fields set. * * Keep this private because ManualScaling is mutable. */ private static final ManualScaling EMPTY_SETTINGS = new ManualScaling(); private String instances; public String getInstances() { return instances; } /** * Sets instances. Normalizes empty and null inputs to null. */ public void setInstances(String instances) { this.instances = toNullIfEmptyOrWhitespace(instances); } public boolean isEmpty() { return this.equals(EMPTY_SETTINGS); } @Override public int hashCode() { return Objects.hash(instances); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } ManualScaling other = (ManualScaling) obj; return Objects.equals(instances, other.instances); } @Override public String toString() { return "ManualScaling [" + "instances=" + instances + "]"; } } /** * Holder for basic settings. */ public static class BasicScaling { /* * BasicScaling with no fields set. * * Keep this private because BasicScaling is mutable. */ private static final BasicScaling EMPTY_SETTINGS = new BasicScaling(); private String maxInstances; private String idleTimeout; public String getMaxInstances() { return maxInstances; } public String getIdleTimeout() { return idleTimeout; } /** * Sets maxInstances. Normalizes empty and null inputs to null. */ public void setMaxInstances(String maxInstances) { this.maxInstances = toNullIfEmptyOrWhitespace(maxInstances); } /** * Sets idleTimeout. Normalizes empty and null inputs to null. */ public void setIdleTimeout(String idleTimeout) { this.idleTimeout = toNullIfEmptyOrWhitespace(idleTimeout); } public boolean isEmpty() { return this.equals(EMPTY_SETTINGS); } @Override public int hashCode() { return Objects.hash(maxInstances, idleTimeout); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } BasicScaling other = (BasicScaling) obj; return Objects.equals(maxInstances, other.maxInstances) && Objects.equals(idleTimeout, other.idleTimeout); } @Override public String toString() { return "BasicScaling [" + "maxInstances=" + maxInstances + ", idleTimeout=" + idleTimeout + "]"; } } public static class ClassLoaderConfig { private final List<PrioritySpecifierEntry> entries = Lists.newArrayList(); public void add(PrioritySpecifierEntry entry) { entries.add(entry); } public List<PrioritySpecifierEntry> getEntries() { return entries; } // Generated by eclipse. @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((entries == null) ? 0 : entries.hashCode()); return result; } // Generated by eclipse. @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } ClassLoaderConfig other = (ClassLoaderConfig) obj; if (entries == null) { if (other.entries != null) { return false; } } else if (!entries.equals(other.entries)) { return false; } return true; } @Override public String toString() { return "ClassLoaderConfig{entries=\"" + entries + "\"}"; } } public static class PrioritySpecifierEntry { private String filename; private Double priority; // null means not present. Default priority is 1.0. private void checkNotAlreadySet() { if (filename != null) { throw new AppEngineConfigException( "Found more that one file name matching tag. " + "Only one of 'filename' attribute allowed."); } } public String getFilename() { return filename; } public void setFilename(String filename) { checkNotAlreadySet(); this.filename = filename; } public Double getPriority() { return priority; } // Returns the priority of this specifier or the assumed default priority // value if it is not specified. public double getPriorityValue() { if (priority == null) { return 1.0d; } return priority; } public void setPriority(String priority) { if (this.priority != null) { throw new AppEngineConfigException("The 'priority' tag may only be specified once."); } if (priority == null) { this.priority = null; return; } this.priority = Double.parseDouble(priority); } // Check that this is a valid ClassLoaderConfig public void checkClassLoaderConfig() { if (filename == null) { throw new AppEngineConfigException("Must have a filename attribute."); } } // Generated by eclipse. @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((filename == null) ? 0 : filename.hashCode()); result = prime * result + ((priority == null) ? 0 : priority.hashCode()); return result; } // Generated by eclipse. @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } PrioritySpecifierEntry other = (PrioritySpecifierEntry) obj; if (filename == null) { if (other.filename != null) { return false; } } else if (!filename.equals(other.filename)) { return false; } if (priority == null) { if (other.priority != null) { return false; } } else if (!priority.equals(other.priority)) { return false; } return true; } @Override public String toString() { return "PrioritySpecifierEntry{filename=\"" + filename + "\", priority=\"" + priority + "\"}"; } } }