1 /*
2  * Copyright 2016-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */

15 package com.amazonaws.partitions;
16
17 import com.amazonaws.partitions.model.Endpoint;
18 import com.amazonaws.partitions.model.Partition;
19 import com.amazonaws.partitions.model.Service;
20 import com.amazonaws.regions.RegionImpl;
21 import com.amazonaws.util.ValidationUtils;
22
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.concurrent.ConcurrentHashMap;
30
31 /**
32  * A region implementation backed by the partition.
33  */

34 public class PartitionRegionImpl implements RegionImpl {
35
36     private static final String SERVICE = "{service}";
37     private static final String REGION = "{region}";
38     private static final String DNS_SUFFIX = "{dnsSuffix}";
39
40     /**
41      * partition where the region is present.
42      */

43     private final Partition partition;
44
45     /**
46      * the name of the region.
47      */

48     private final String region;
49
50     /**
51      * endpoint cache for a service.
52      */

53     private final Map<String, Endpoint> endpointCache = new ConcurrentHashMap<String,
54                 Endpoint>();
55
56     public PartitionRegionImpl(String region, Partition p) {
57         this.partition = ValidationUtils.assertNotNull(p, "partition");
58         this.region = ValidationUtils.assertNotNull(region, "region");
59     }
60
61     @Override
62     public String getName() {
63         return region;
64     }
65
66     @Override
67     public String getDomain() {
68         return partition.getDnsSuffix();
69     }
70
71     @Override
72     public String getPartition() {
73         return partition.getPartition();
74     }
75
76     /**
77      * Returns the endpoint for the given service.
78      * If the region is not present under service configuration; but matches
79      * the region regex, then this system tries to guess the endpoint and
80      * returns it.
81      */

82     @Override
83     public String getServiceEndpoint(String serviceName) {
84         return getEndpointString(serviceName, getEndpoint(serviceName));
85     }
86
87     /**
88      * Returns the endpoint configuration for a given service.
89      */

90     private Endpoint getEndpoint(String serviceName) {
91         Endpoint cachedEndpoint = endpointCache.get(serviceName);
92
93         if (cachedEndpoint == null) {
94             cachedEndpoint = computeEndpoint(serviceName);
95             if (cachedEndpoint == null) {
96                 return null;
97             }
98         }
99
100         endpointCache.put(serviceName, cachedEndpoint);
101         return cachedEndpoint;
102     }
103
104     private Endpoint computeEndpoint(String serviceName) {
105
106         Service service = partition.getServices().get(serviceName);
107
108
109         if (service != null) {
110             if (service.getEndpoints().containsKey(region)) {
111
112                 Endpoint merged = Endpoint.merge(
113                         partition.getDefaults(),
114                         Endpoint.merge(service.getDefaults(),
115                                 service.getEndpoints().get(region)));
116
117                 return merged;
118
119             } else if (service.isPartitionWideEndpointAvailable() && !service
120                         .isRegionalized()) {
121                 // partition doesn't have any information about a service.
122
123                 Endpoint merged = Endpoint.merge(
124                         partition.getDefaults(),
125                         Endpoint.merge(service.getDefaults(),
126                                 service.getEndpoints().get(
127                                         service.getPartitionEndpoint())));
128
129                 return merged;
130
131             } else {
132                 if (partition.getDefaults() != null
133                         && partition.getDefaults().getHostName() != null) {
134                     return partition.getDefaults();
135                 }
136             }
137         }
138         return null;
139     }
140
141     private String getEndpointString(String serviceName, Endpoint endpoint) {
142         return endpoint == null
143                 ? null
144                 : endpoint.getHostName()
145                 .replace(SERVICE, serviceName)
146                 .replace(REGION, region)
147                 .replace(DNS_SUFFIX, partition.getDnsSuffix());
148     }
149
150     /**
151      * Returns true if the service is explicitly enabled for a region or a
152      * service is partition wide enabled. Returns false otherwise.
153      * <p/>
154      * For new regions that match the partition's region regex, if the
155      * service is enabled partition wide then this method will return true.
156      */

157     @Override
158     public boolean isServiceSupported(String serviceName) {
159         return isServiceSupportedInRegion(serviceName) || isServicePartitionWide
160                 (serviceName);
161     }
162
163     /**
164      * This method returns true only if the metadata for the service contains the given
165      * region in the list of supported regions.
166      */

167     private boolean isServiceSupportedInRegion(String serviceName) {
168         return partition.getServices().get(serviceName) != null
169                 && partition.getServices().get(serviceName).getEndpoints()
170                 .containsKey(region);
171     }
172
173     private boolean isServicePartitionWide(String serviceName) {
174         return partition.getServices().get(serviceName) != null
175                 && partition.getServices().get(serviceName)
176                 .getPartitionEndpoint() != null;
177     }
178
179     /**
180      * Returns true if the service is explicitly enabled for https in this
181      * region or a service is partition wide enabled. Returns false otherwise.
182      * <p/>
183      * For new regions that match the partition's region regex, if the
184      * service is enabled partition wide then this method will return true.
185      */

186     @Override
187     public boolean hasHttpsEndpoint(String serviceName) {
188
189         if (!isServiceSupported(serviceName)) return false;
190
191         return getEndpoint(serviceName).hasHttpsSupport();
192
193     }
194
195     /**
196      * Returns true if the service is explicitly enabled for http in this
197      * region or a service is partition wide enabled. Returns false otherwise.
198      * <p/>
199      * For new regions that match the partition's region regex, if the
200      * service is enabled partition wide then this method will return true.
201      */

202     @Override
203     public boolean hasHttpEndpoint(String serviceName) {
204         if (!isServiceSupported(serviceName)) return false;
205
206         return getEndpoint(serviceName).hasHttpSupport();
207     }
208
209     @Override
210     public Collection<String> getAvailableEndpoints() {
211         final List<String> endpoints = new ArrayList<String>();
212         for (String service : partition.getServices().keySet()) {
213             if (isServiceSupported(service)) {
214                 endpoints.add(getServiceEndpoint(service));
215             }
216         }
217         return Collections.unmodifiableCollection(endpoints);
218     }
219 }
220