Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OLPSUP-27402 cache API Lookup response in temp file #61

Merged
merged 1 commit into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,9 @@
import com.here.platform.artifact.maven.wagon.model.RegisterRequest;
import com.here.platform.artifact.maven.wagon.model.RegisterResponse;
import com.here.platform.artifact.maven.wagon.model.ServiceExceptionResponse;
import com.here.platform.artifact.maven.wagon.resolver.ArtifactWagonPropertiesResolver;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpStatus;
import com.here.platform.artifact.maven.wagon.resolver.ArtifactServiceUrlResolverChain;
import com.here.platform.artifact.maven.wagon.util.StringUtils;
import org.apache.http.*;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
Expand Down Expand Up @@ -68,11 +64,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.net.URI;
import java.util.Optional;
import java.util.Properties;
Expand Down Expand Up @@ -115,17 +107,12 @@ public class ArtifactWagon extends AbstractHttpClientWagon {
* Defines the protocol mapping to use. NOTE: The order of the mapping becomes the search order.
*/
private static final String[][] PROTOCOL_MAP =
new String[][] {
{"here+http://", "http://"}, // http and here auth
{"here+https://", "https://"}, // https and here auth
new String[][]{
{"here+http://", "http://"}, // http and here auth
{"here+https://", "https://"}, // https and here auth
};

private static final String ARTIFACT_SERVICE_URL_PLACEHOLDER_PROTOCOL = "here+artifact-service";
/**
* Defines the Artifact Service url to use.
* NOTE: The variable is static so that the url is pulled only the first time and is reused for all other dependencies..
*/
private static String defaultArtifactServiceUrl;
private final Object lock = new Object();
private final ObjectMapper objectMapper;
private final Properties hereProperties;
Expand All @@ -144,29 +131,24 @@ public ArtifactWagon() throws IllegalAccessException {
}

String getDefaultArtifactServiceUrl() {
if(defaultArtifactServiceUrl == null) {
// resolve hrnPrefix and artifactServiceUrl by here token endpoint url.
try (CloseableHttpClient httpclient = createProxyAwareHttpClient()) {
String hereTokenEndpointUrl = this.hereProperties.getProperty(HERE_ENDPOINT_URL_KEY);
ArtifactWagonPropertiesResolver environmentPropertiesResolver = new ArtifactWagonPropertiesResolver(httpclient::execute, objectMapper);
defaultArtifactServiceUrl = environmentPropertiesResolver.resolveArtifactServiceUrl(hereTokenEndpointUrl);
} catch (IOException e) {
throw new RuntimeException(e);
}
String hereTokenEndpointUrl = this.hereProperties.getProperty(HERE_ENDPOINT_URL_KEY);
ArtifactServiceUrlResolverChain artifactServiceUrlResolverChain = new ArtifactServiceUrlResolverChain(this::createProxyAwareHttpClient, objectMapper);
String artifactServiceUrl = artifactServiceUrlResolverChain.resolveArtifactServiceUrl(hereTokenEndpointUrl);
if (StringUtils.isEmpty(artifactServiceUrl)) {
throw new RuntimeException("Unable to resolve Artifact Service URL");
}
return defaultArtifactServiceUrl;
return artifactServiceUrl;
}

private CloseableHttpClient createProxyAwareHttpClient() {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create()
.addInterceptorFirst((HttpRequest request, HttpContext context) ->
{
.addInterceptorFirst((HttpRequest request, HttpContext context) -> {
if (request instanceof HttpUriRequest) {
setHeaders((HttpUriRequest) request);
}
});
});
ProxyInfo proxyInfo = getProxyInfo();
if(proxyInfo != null) {
if (proxyInfo != null) {
String proxyHost = proxyInfo.getHost();
int proxyport = proxyInfo.getPort();
BasicCredentialsProvider basicCredentialsProvider = new BasicCredentialsProvider();
Expand All @@ -177,7 +159,6 @@ private CloseableHttpClient createProxyAwareHttpClient() {
.setDefaultCredentialsProvider(basicCredentialsProvider)
.setProxy(new HttpHost(proxyHost, proxyport));
}

return httpClientBuilder.build();
}

Expand Down Expand Up @@ -277,7 +258,7 @@ public void put(File source, String resourceName)
super.put(source, resourceName);
} catch (AuthorizationException e) {
throw new AuthorizationException(AUTHORIZATION_FORBIDDEN_ERROR_MESSAGE, e);
} catch (RuntimeException re){
} catch (RuntimeException re) {
throw new TransferFailedException(String.format(FILE_PUT_ERROR_MESSAGE, resourceName), re);
}
}
Expand All @@ -286,17 +267,17 @@ public void put(File source, String resourceName)
protected CloseableHttpResponse execute(HttpUriRequest httpMethod) throws HttpException, IOException {
CloseableHttpResponse httpResponse = super.execute(httpMethod);

int status = httpResponse.getStatusLine().getStatusCode();
if (status == HttpStatus.SC_UNPROCESSABLE_ENTITY) {
String message = "";
String content = EntityUtils.toString(httpResponse.getEntity());
if (!content.isEmpty()) {
ServiceExceptionResponse exceptionResponse =
objectMapper.readValue(content, ServiceExceptionResponse.class);
message = exceptionResponse.getMessage();
}
throw new RuntimeException(httpResponse.getStatusLine() + " " + message);
int status = httpResponse.getStatusLine().getStatusCode();
if (status == HttpStatus.SC_UNPROCESSABLE_ENTITY) {
String message = "";
String content = EntityUtils.toString(httpResponse.getEntity());
if (!content.isEmpty()) {
ServiceExceptionResponse exceptionResponse =
objectMapper.readValue(content, ServiceExceptionResponse.class);
message = exceptionResponse.getMessage();
}
throw new RuntimeException(httpResponse.getStatusLine() + " " + message);
}

return httpResponse;
}
Expand Down Expand Up @@ -488,7 +469,7 @@ private String mintAuthorizationToken(Properties properties) {
protected Properties loadHereProperties() {
Properties properties = new Properties();
File file = resolveFile();
if(file != null) {
if (file != null) {
loadCredentialsFromFile(properties, file);
}
String credentialsString = System.getenv(HERE_CREDENTIALS_STRING_ENV);
Expand Down Expand Up @@ -604,6 +585,7 @@ private void consumeQuietly(CloseableHttpResponse httpResponse) {
* Here we set retry strategy for internal http client. This workaround is used in order to avoid using
* of 'maven.wagon.http.serviceUnavailableRetryStrategy.class' environment variable
* That environment variable might lead to ClassNotFoundException with Maven 3.8.1
*
* @see <a href="https://github.com/apache/maven-wagon/pull/57">WAGON-567</a>
*/
private void setRetryStrategy() throws IllegalAccessException {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright (C) 2018-2023 HERE Europe B.V.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
* License-Filename: LICENSE
*/
package com.here.platform.artifact.maven.wagon.resolver;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.Duration;
import java.time.Instant;
import java.util.Properties;

/**
* Return Artifact Service URL cached in temporary file
*/
class ArtifactServiceUrlFileResolver implements ArtifactServiceUrlResolver {

private static final Logger LOG = LoggerFactory.getLogger(ArtifactServiceUrlFileResolver.class);

private static final String FILE_NAME = "artifact_wagon_url_cache.properties";

private static final long MAX_FILE_AGE_MINUTES = 5;

@Override
public String resolveArtifactServiceUrl(String tokenUrl) {
try {
Properties prop = loadCachedProperties(getCacheFile());
return prop.getOrDefault(tokenUrl, "").toString();
} catch (Exception e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Can't resolve Artifact Service URL from file", e);
} else {
LOG.warn("Can't resolve Artifact Service URL from file");
}
return "";
}
}

@Override
public void afterUrlResolved(String tokenUrl, String resolvedUrl) {
try {
Path cacheFile = getCacheFile();
Properties prop = loadCachedProperties(cacheFile);
try (OutputStream output = Files.newOutputStream(cacheFile)) {
prop.put(tokenUrl, resolvedUrl);
prop.store(output, "Artifact Service URL cache");
}
} catch (Exception e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Failed to update Artifact Service URL in temp file", e);
}
}
}

private Properties loadCachedProperties(Path path) throws IOException {
try (InputStream input = Files.newInputStream(path)) {
Properties prop = new Properties();
prop.load(input);
return prop;
}
}

private Path getCacheFile() throws IOException {
Path path = Paths.get(System.getProperty("java.io.tmpdir"), FILE_NAME);
File file = path.toFile();
if (!file.createNewFile()) {
if (isFileOlderThan(path, MAX_FILE_AGE_MINUTES)) {
file.delete();
file.createNewFile();
}
}
return path;
}

private boolean isFileOlderThan(Path path, long minutes) throws IOException {
Instant fileInstant = Files.readAttributes(path, BasicFileAttributes.class).creationTime().toInstant();
Instant now = Instant.now();
Duration difference = Duration.between(fileInstant, now);
return difference.toMinutes() > minutes;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2018-2023 HERE Europe B.V.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
* License-Filename: LICENSE
*/
package com.here.platform.artifact.maven.wagon.resolver;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* Return in memory cached Artifact Service URL based on here token URL.
*/
class ArtifactServiceUrlInMemoryCachedResolver implements ArtifactServiceUrlResolver {

/**
* Store the Artifact Service URL to use.
* NOTE: The variable is static so that the url is pulled only the first time and is reused for all other dependencies.
*/
private static final Map<String, String> tokenUrlToArtSvcUrl = new ConcurrentHashMap<>();

@Override
public String resolveArtifactServiceUrl(String tokenUrl) {
return tokenUrlToArtSvcUrl.get(tokenUrl);
}

@Override
public void afterUrlResolved(String tokenUrl, String resolvedUrl) {
tokenUrlToArtSvcUrl.put(tokenUrl, resolvedUrl);
}

}
Loading
Loading