Fix Sonar complains
[oam/nf-oam-adopter.git] / ves-nf-oam-adopter / ves-nf-oam-adopter-pm-sb-rest-client / src / main / java / org / o / ran / oam / nf / oam / adopter / pm / sb / rest / client / DefaultHttpRestClient.java
1 /*
2  *  ============LICENSE_START=======================================================
3  *  O-RAN-SC
4  *  ================================================================================
5  *  Copyright © 2021 AT&T Intellectual Property. All rights reserved.
6  *  ================================================================================
7  *  Licensed under the Apache License, Version 2.0 (the "License");
8  *  you may not use this file except in compliance with the License.
9  *  You may obtain a copy of the License at
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  *  ============LICENSE_END=========================================================
18  */
19
20 package org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client;
21
22 import static org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.http.DownloadPerformanceManagementFilesHandler.readPerformanceManagementFiles;
23 import static org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.http.OffSetTimeZoneHandler.readTimeZone;
24 import static org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.http.TokenHandler.returnToken;
25 import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
26
27 import com.google.common.cache.CacheBuilder;
28 import com.google.common.cache.CacheLoader;
29 import com.google.common.cache.LoadingCache;
30 import io.reactivex.rxjava3.core.Single;
31 import java.time.Instant;
32 import java.time.ZoneId;
33 import java.time.format.DateTimeFormatter;
34 import java.util.concurrent.ExecutionException;
35 import java.util.concurrent.TimeUnit;
36 import java.util.zip.ZipInputStream;
37 import org.apache.hc.client5.http.async.methods.SimpleHttpRequests;
38 import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
39 import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
40 import org.apache.hc.core5.http.ConnectionClosedException;
41 import org.apache.hc.core5.http.HttpHeaders;
42 import org.checkerframework.checker.lock.qual.GuardedBy;
43 import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.api.HttpRestClient;
44 import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.PerformanceManagementException;
45 import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.TokenGenerationException;
46 import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.ZoneIdException;
47 import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.Adapter;
48 import org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.properties.PmEndpointsUrlsProperties;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51 import org.springframework.beans.factory.annotation.Autowired;
52 import org.springframework.stereotype.Service;
53
54 @Service
55 public final class DefaultHttpRestClient implements HttpRestClient {
56     private static final Logger LOG = LoggerFactory.getLogger(DefaultHttpRestClient.class);
57
58     public static final String HTTPS = "https://";
59     public static final String BEARER = "Bearer ";
60     private static final DateTimeFormatter OFFSET_FORMATTER = DateTimeFormatter.ofPattern("xxx");
61     private final CloseableHttpAsyncClient client;
62     @GuardedBy("this")
63     private final LoadingCache<Adapter, String> sessionCache =
64             CacheBuilder.newBuilder().refreshAfterWrite(59, TimeUnit.MINUTES).build(new CacheLoader<>() {
65                 @Override
66                 public String load(final Adapter adapter) throws ExecutionException, InterruptedException {
67                     return returnToken(DefaultHttpRestClient.this.client, DefaultHttpRestClient.this.tokenEndpoint,
68                             adapter);
69                 }
70             });
71
72     @GuardedBy("this")
73     private final LoadingCache<Adapter, ZoneId> zoneIdCache =
74             CacheBuilder.newBuilder().build(new CacheLoader<>() {
75                 @Override
76                 public ZoneId load(final Adapter adapter) {
77                     return readTimeZone(DefaultHttpRestClient.this, timeZoneEndpoint,  adapter)
78                                    .doOnError(error -> LOG.error("Failed to read time zone", error))
79                                    .blockingGet();
80                 }
81             });
82     private final String pmFilesEndpoint;
83     private final String timeZoneEndpoint;
84     private final String tokenEndpoint;
85
86     /**
87      * Default constructor.
88      */
89     @Autowired
90     public DefaultHttpRestClient(final CloseableHttpAsyncClient httpAsyncClient,
91             final PmEndpointsUrlsProperties properties) {
92         this.client = httpAsyncClient;
93         this.pmFilesEndpoint = properties.getRanPmEndpoint();
94         this.timeZoneEndpoint = properties.getRanTimeZoneOffsetEndpoint();
95         this.tokenEndpoint = properties.getRanTokenEndpoint();
96     }
97
98
99     @Override
100     public synchronized Single<ZipInputStream> readFiles(final Adapter adapter) {
101         return readPerformanceManagementFiles(this, pmFilesEndpoint, adapter);
102     }
103
104     @Override
105     public Single<ZoneId> getTimeZone(final Adapter adapter) {
106         try {
107             final var zoneId = zoneIdCache.get(adapter);
108             final String offset = OFFSET_FORMATTER.format(zoneId.getRules().getOffset(Instant.now()));
109             LOG.info("Adapter {} has offset {}", adapter.getHostIpAddress(), offset);
110             return Single.just(zoneId);
111         } catch (final Exception e) {
112             final Throwable cause = e.getCause();
113             if (cause instanceof PerformanceManagementException) {
114                 return Single.error(cause);
115             }
116             return Single.error(new ZoneIdException("Failed to get Zone ID for " + adapter.getHostIpAddress(), cause));
117         }
118     }
119
120     /**
121      * Execute GET request on adapter endpoint.
122      * @param adapter destiny
123      * @param url endpoint
124      * @return response
125      */
126     public Single<SimpleHttpResponse> get(final Adapter adapter, final String url) {
127         return getToken(adapter).flatMap(token -> {
128             final var request = SimpleHttpRequests.get(HTTPS + adapter.getHostIpAddress() + url);
129             request.addHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON_VALUE);
130             request.addHeader(HttpHeaders.AUTHORIZATION, BEARER + token);
131             return Single.fromFuture(client.execute(request, null))
132                     .doOnSubscribe(result -> LOG.trace("GET Request started {} ...", request))
133                     .doOnSuccess(result -> LOG.trace("GET Request finished {}", request));
134         });
135     }
136
137     private Single<String> getToken(final Adapter adapter) {
138         try {
139             final String token = sessionCache.get(adapter);
140             return Single.just(token);
141         } catch (final Exception e) {
142             if (e.getCause() instanceof TokenGenerationException || e.getCause() instanceof ConnectionClosedException) {
143                 return Single.error(e.getCause());
144             }
145             return Single.error(e);
146         }
147     }
148 }