View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.search.backend.remoterepository.internal;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.net.URI;
24  import java.net.http.HttpClient;
25  import java.net.http.HttpRequest;
26  import java.net.http.HttpResponse;
27  import java.time.Duration;
28  import java.util.AbstractMap;
29  import java.util.Map;
30  import java.util.stream.Collectors;
31  
32  import org.apache.maven.search.backend.remoterepository.RemoteRepositorySearchTransport;
33  
34  import static java.util.Objects.requireNonNull;
35  
36  /**
37   * Java 11 {@link HttpClient} backed transport.
38   */
39  public class Java11HttpClientRemoteRepositorySearchTransport implements RemoteRepositorySearchTransport {
40      private static class ResponseImpl implements Response {
41  
42          private final HttpResponse<?> response;
43  
44          private final InputStream inputStream;
45  
46          private ResponseImpl(HttpResponse<?> response, InputStream inputStream) {
47              this.response = requireNonNull(response);
48              this.inputStream = inputStream;
49          }
50  
51          @Override
52          public int getCode() {
53              return response.statusCode();
54          }
55  
56          @Override
57          public Map<String, String> getHeaders() {
58              return response.headers().map().entrySet().stream()
59                      .map(e -> new AbstractMap.SimpleEntry<>(
60                              e.getKey(), e.getValue().get(0)))
61                      .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
62          }
63  
64          @Override
65          public InputStream getBody() {
66              return inputStream;
67          }
68  
69          @Override
70          public void close() throws IOException {
71              if (inputStream != null) {
72                  inputStream.close();
73              }
74          }
75      }
76  
77      private final Duration timeout;
78  
79      private final HttpClient client;
80  
81      public Java11HttpClientRemoteRepositorySearchTransport() {
82          this(Duration.ofSeconds(10L));
83      }
84  
85      public Java11HttpClientRemoteRepositorySearchTransport(Duration timeout) {
86          this(
87                  timeout,
88                  HttpClient.newBuilder()
89                          .connectTimeout(timeout)
90                          .followRedirects(HttpClient.Redirect.NEVER)
91                          .build());
92      }
93  
94      public Java11HttpClientRemoteRepositorySearchTransport(Duration timeout, HttpClient client) {
95          this.timeout = requireNonNull(timeout);
96          this.client = requireNonNull(client);
97      }
98  
99      @Override
100     public Response get(String serviceUri, Map<String, String> headers) throws IOException {
101         HttpRequest.Builder builder = HttpRequest.newBuilder()
102                 .timeout(timeout)
103                 .uri(URI.create(serviceUri))
104                 .GET();
105         for (Map.Entry<String, String> header : headers.entrySet()) {
106             builder.header(header.getKey(), header.getValue());
107         }
108         HttpRequest request = builder.build();
109         try {
110             HttpResponse<InputStream> response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
111             return new ResponseImpl(response, response.body());
112         } catch (InterruptedException e) {
113             Thread.currentThread().interrupt();
114             throw new IOException(e);
115         }
116     }
117 
118     @Override
119     public Response head(String serviceUri, Map<String, String> headers) throws IOException {
120         HttpRequest.Builder builder = HttpRequest.newBuilder()
121                 .timeout(timeout)
122                 .uri(URI.create(serviceUri))
123                 .method("HEAD", HttpRequest.BodyPublishers.noBody());
124         for (Map.Entry<String, String> header : headers.entrySet()) {
125             builder.header(header.getKey(), header.getValue());
126         }
127         HttpRequest request = builder.build();
128         try {
129             HttpResponse<Void> response = client.send(request, HttpResponse.BodyHandlers.discarding());
130             return new ResponseImpl(response, null);
131         } catch (InterruptedException e) {
132             Thread.currentThread().interrupt();
133             throw new IOException(e);
134         }
135     }
136 }