Java tutorial
/* * 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.api.server.spi.config.model; import com.google.api.server.spi.Constant; import com.google.api.server.spi.ServiceContext; import com.google.api.server.spi.TypeLoader; import com.google.api.server.spi.config.ApiConfigInconsistency; import com.google.api.server.spi.config.AuthLevel; import com.google.api.server.spi.config.Authenticator; import com.google.api.server.spi.config.PeerAuthenticator; import com.google.api.server.spi.config.scope.AuthScopeExpression; import com.google.api.server.spi.config.scope.AuthScopeExpressions; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; /** * Flattened configuration for a swarm endpoint. Data generally originates from * {@link com.google.api.server.spi.config.Api} annotations. * * @author Eric Orth */ // TODO: Clean things up a bit by separating it into an immutable config object and a // Builder, and merge the new vs copy creation by having the new just copy a default config. Then // efficientisize config reload make it so the builder only copies once an actual change is // detected. public class ApiConfig { // Default value of scopes and clientIds if unset private static final AuthScopeExpression DEFAULT_SCOPE_EXPRESSION = AuthScopeExpressions .interpret(Constant.API_EMAIL_SCOPE); private static final List<String> DEFAULT_CLIENT_IDS = ImmutableList.of(Constant.API_EXPLORER_CLIENT_ID); private final TypeLoader typeLoader; private String root; private String name; private String canonicalName; private String version; private String title; private String description; private String documentationLink; private String backendRoot; private boolean isAbstract; private boolean defaultVersion; private boolean discoverable; private String resource; private boolean useDatastore; private AuthLevel authLevel; private AuthScopeExpression scopeExpression; private List<String> audiences; private ApiIssuerConfigs issuers; private ApiIssuerAudienceConfig issuerAudiences; private List<String> clientIds; private List<Class<? extends Authenticator>> authenticators; private List<Class<? extends PeerAuthenticator>> peerAuthenticators; private boolean apiKeyRequired; private final ApiAuthConfig authConfig; private final ApiCacheControlConfig cacheControlConfig; private final ApiFrontendLimitsConfig frontendLimitsConfig; private final ApiSerializationConfig serializationConfig; private final ApiNamespaceConfig namespaceConfig; private final ApiClassConfig apiClassConfig; /** * Simple factory to create {@link ApiConfig} instances. */ public static class Factory { public ApiConfig create(ServiceContext serviceContext, TypeLoader typeLoader, Class<?> endpointClass) { return new ApiConfig(serviceContext, typeLoader, endpointClass); } public ApiConfig copy(ApiConfig old) { return new ApiConfig(old); } } /** * Hidden constructor. Instantiate using {@link Factory}. */ protected ApiConfig(ServiceContext serviceContext, TypeLoader typeLoader, Class<?> apiClass) { this.typeLoader = typeLoader; authConfig = createAuthConfig(); cacheControlConfig = createCacheControlConfig(); frontendLimitsConfig = createFrontendLimitsConfig(); serializationConfig = createSerializationConfig(); namespaceConfig = createNamespaceConfig(); apiClassConfig = createApiClassConfig(typeLoader, apiClass); setDefaults(serviceContext); } /** * Hidden copy constructor. Use {@link Factory}. */ protected ApiConfig(ApiConfig original) { this.typeLoader = original.typeLoader; this.root = original.root; this.name = original.name; this.canonicalName = original.canonicalName; this.version = original.version; this.title = original.title; this.description = original.description; this.documentationLink = original.documentationLink; this.backendRoot = original.backendRoot; this.isAbstract = original.isAbstract; this.defaultVersion = original.defaultVersion; this.discoverable = original.discoverable; this.resource = original.resource; this.useDatastore = original.useDatastore; this.authLevel = original.authLevel; this.scopeExpression = original.scopeExpression; this.audiences = original.audiences == null ? null : new ArrayList<>(original.audiences); this.issuers = original.issuers; this.issuerAudiences = original.issuerAudiences; this.clientIds = original.clientIds == null ? null : new ArrayList<>(original.clientIds); this.authenticators = original.authenticators; this.peerAuthenticators = original.peerAuthenticators; this.apiKeyRequired = original.apiKeyRequired; this.authConfig = new ApiAuthConfig(original.authConfig); this.cacheControlConfig = new ApiCacheControlConfig(original.cacheControlConfig); this.frontendLimitsConfig = new ApiFrontendLimitsConfig(original.frontendLimitsConfig); this.serializationConfig = new ApiSerializationConfig(original.serializationConfig); this.namespaceConfig = new ApiNamespaceConfig(original.namespaceConfig); this.apiClassConfig = new ApiClassConfig(original.apiClassConfig, this); } @Override public boolean equals(Object o) { if (this == o) { return true; } else if (o instanceof ApiConfig) { ApiConfig config = (ApiConfig) o; return Iterables.isEmpty(getConfigurationInconsistencies(config)) && apiClassConfig.equals(config.apiClassConfig); } else { return false; } } /** * @return {@code true} if all API-level (not class or method specific) configuration is * identical. */ public Iterable<ApiConfigInconsistency<Object>> getConfigurationInconsistencies(ApiConfig config) { return ApiConfigInconsistency.listBuilder().addIfInconsistent("typeLoader", typeLoader, config.typeLoader) .addIfInconsistent("root", root, config.root).addIfInconsistent("name", name, config.name) .addIfInconsistent("cannonicalName", canonicalName, config.canonicalName) .addIfInconsistent("version", version, config.version) .addIfInconsistent("title", title, config.title) .addIfInconsistent("description", description, config.description) .addIfInconsistent("documentationLink", documentationLink, config.documentationLink) .addIfInconsistent("backendRoot", backendRoot, config.backendRoot) .addIfInconsistent("isAbstract", isAbstract, config.isAbstract) .addIfInconsistent("defaultVersion", defaultVersion, config.defaultVersion) .addIfInconsistent("discoverable", discoverable, config.discoverable) .addIfInconsistent("useDatastore", useDatastore, config.useDatastore) .addIfInconsistent("resource", resource, config.resource) .addIfInconsistent("authLevel", authLevel, config.authLevel) .addIfInconsistent("scopeExpression", scopeExpression, config.scopeExpression) .addIfInconsistent("audiences", audiences, config.audiences) .addIfInconsistent("issuers", issuers, config.issuers) .addIfInconsistent("issuerAudiencies", issuerAudiences, config.issuerAudiences) .addIfInconsistent("clientIds", clientIds, config.clientIds) .addIfInconsistent("authenticators", authenticators, config.authenticators) .addIfInconsistent("peerAuthenticators", peerAuthenticators, config.peerAuthenticators) .addIfInconsistent("apiKeyRequired", apiKeyRequired, config.apiKeyRequired) .addAll(authConfig.getConfigurationInconsistencies(config.authConfig)) .addAll(cacheControlConfig.getConfigurationInconsistencies(config.cacheControlConfig)) .addAll(frontendLimitsConfig.getConfigurationInconsistencies(config.frontendLimitsConfig)) .addAll(serializationConfig.getConfigurationInconsistencies(config.serializationConfig)) .addAll(namespaceConfig.getConfigurationInconsistencies(config.namespaceConfig)).build(); } @Override public int hashCode() { return Objects.hash(typeLoader, root, name, canonicalName, version, title, description, documentationLink, backendRoot, isAbstract, defaultVersion, discoverable, useDatastore, resource, authLevel, scopeExpression, audiences, clientIds, authenticators, peerAuthenticators, authConfig, cacheControlConfig, frontendLimitsConfig, serializationConfig, apiClassConfig, issuers, issuerAudiences, apiKeyRequired); } /** * Creates the auth configuration object. Override to use a subclass instead. */ protected ApiAuthConfig createAuthConfig() { return new ApiAuthConfig(); } /** * Creates the cache control configuration object. Override to use a subclass instead. */ protected ApiCacheControlConfig createCacheControlConfig() { return new ApiCacheControlConfig(); } /** * Creates the frontend limits configuration object. Override to use a subclass instead. */ protected ApiFrontendLimitsConfig createFrontendLimitsConfig() { return new ApiFrontendLimitsConfig(); } /** * Creates the serialization configuration object. Override to use a subclass instead. */ protected ApiSerializationConfig createSerializationConfig() { return new ApiSerializationConfig(); } /** * Creates the namespace configuration object. Override to use a subclass instead. */ protected ApiNamespaceConfig createNamespaceConfig() { return new ApiNamespaceConfig(); } protected ApiClassConfig createApiClassConfig(TypeLoader typeLoader, Class<?> apiClass) { return new ApiClassConfig(this, typeLoader, apiClass); } /** * Sets all fields to their default value to be used if not set otherwise. Override to change the * default configuration. */ protected void setDefaults(ServiceContext serviceContext) { root = serviceContext.getTransferProtocol() + "://" + serviceContext.getAppHostName() + "/_ah/api"; name = serviceContext.getDefaultApiName(); canonicalName = null; version = "v1"; description = null; backendRoot = serviceContext.getTransferProtocol() + "://" + serviceContext.getAppHostName() + "/_ah/spi"; isAbstract = false; defaultVersion = false; discoverable = true; useDatastore = false; resource = null; authLevel = AuthLevel.NONE; scopeExpression = DEFAULT_SCOPE_EXPRESSION; audiences = Collections.emptyList(); issuers = ApiIssuerConfigs.EMPTY; issuerAudiences = ApiIssuerAudienceConfig.EMPTY; clientIds = DEFAULT_CLIENT_IDS; authenticators = null; peerAuthenticators = null; apiKeyRequired = false; } public ApiKey getApiKey() { return new ApiKey(name, version, root); } public void setRoot(String root) { this.root = root; } public String getRoot() { return root; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setCanonicalName(String canonicalName) { this.canonicalName = canonicalName; } public String getCanonicalName() { return canonicalName; } public void setTitle(String title) { this.title = title; } public String getTitle() { return title; } public void setVersion(String version) { this.version = version; } public String getVersion() { return version; } public void setDescription(String description) { this.description = description; } public String getDescription() { return description; } public void setDocumentationLink(String documentationLink) { this.documentationLink = documentationLink; } public String getDocumentationLink() { return documentationLink; } public void setBackendRoot(String backendRoot) { this.backendRoot = toHttps(backendRoot); } public String getBackendRoot() { return backendRoot; } public void setIsAbstract(boolean isAbstract) { this.isAbstract = isAbstract; } public boolean getIsAbstract() { return isAbstract; } public void setIsDefaultVersion(boolean defaultVersion) { this.defaultVersion = defaultVersion; } public boolean getIsDefaultVersion() { return defaultVersion; } public void setIsDiscoverable(boolean discoverable) { this.discoverable = discoverable; } public boolean getIsDiscoverable() { return discoverable; } public void setUseDatastore(boolean useDatastore) { this.useDatastore = useDatastore; } public boolean getUseDatastore() { return useDatastore; } public void setResource(String resource) { this.resource = resource; } public String getResource() { return resource; } public ApiAuthConfig getAuthConfig() { return authConfig; } public ApiCacheControlConfig getCacheControlConfig() { return cacheControlConfig; } public ApiFrontendLimitsConfig getFrontendLimitsConfig() { return frontendLimitsConfig; } public ApiSerializationConfig getSerializationConfig() { return serializationConfig; } public ApiNamespaceConfig getNamespaceConfig() { return namespaceConfig; } // TODO: When builder is split off, these get/setters for default auth properties should // only be necessary in the builder. public void setAuthLevel(AuthLevel authLevel) { this.authLevel = authLevel; } public AuthLevel getAuthLevel() { return authLevel; } public void setScopeExpression(AuthScopeExpression scopeExpression) { this.scopeExpression = scopeExpression; } public AuthScopeExpression getScopeExpression() { return scopeExpression; } public void setAudiences(List<String> audiences) { this.audiences = audiences; } public List<String> getAudiences() { return audiences; } public void setIssuers(ApiIssuerConfigs issuers) { this.issuers = issuers; } public void ensureGoogleIssuer() { this.issuers = issuers.withGoogleIdToken(); } public ApiIssuerConfigs getIssuers() { return issuers; } public void setIssuerAudiences(ApiIssuerAudienceConfig issuerAudiences) { Preconditions.checkNotNull(issuerAudiences, "issuerAudiences should never be null"); this.issuerAudiences = issuerAudiences; if (issuerAudiences.hasIssuer(Constant.GOOGLE_ID_TOKEN_NAME)) { ensureGoogleIssuer(); } } public ApiIssuerAudienceConfig getIssuerAudiences() { return issuerAudiences; } public void setClientIds(List<String> clientIds) { this.clientIds = clientIds; } public List<String> getClientIds() { return clientIds; } public void setAuthenticators(List<Class<? extends Authenticator>> authenticators) { this.authenticators = authenticators; } public List<Class<? extends Authenticator>> getAuthenticators() { return authenticators; } public void setPeerAuthenticators(List<Class<? extends PeerAuthenticator>> peerAuthenticators) { this.peerAuthenticators = peerAuthenticators; } public List<Class<? extends PeerAuthenticator>> getPeerAuthenticators() { return peerAuthenticators; } public void setApiKeyRequired(boolean apiKeyRequired) { this.apiKeyRequired = apiKeyRequired; } public boolean isApiKeyRequired() { return apiKeyRequired; } private String toHttps(String url) { if (url != null && url.startsWith("http:")) { return "https:" + url.substring(5); } return url; } public ApiClassConfig getApiClassConfig() { return apiClassConfig; } }