001package dk.netarkivet.common.utils.service; 002 003import java.io.IOException; 004import java.io.InputStream; 005import java.net.MalformedURLException; 006import java.net.URI; 007import java.net.URISyntaxException; 008import java.net.URL; 009import java.net.URLEncoder; 010import java.nio.charset.StandardCharsets; 011import java.nio.file.Path; 012import java.nio.file.Paths; 013import java.util.ArrayList; 014import java.util.List; 015import java.util.regex.Pattern; 016import java.util.stream.Collectors; 017 018import org.apache.commons.io.IOUtils; 019import org.apache.http.client.methods.CloseableHttpResponse; 020import org.apache.http.client.methods.HttpUriRequest; 021import org.apache.http.impl.client.CloseableHttpClient; 022import org.slf4j.Logger; 023import org.slf4j.LoggerFactory; 024 025import dk.netarkivet.common.CommonSettings; 026import dk.netarkivet.common.utils.HttpsClientBuilder; 027import dk.netarkivet.common.utils.Settings; 028 029/** 030 * A FileResolver client to communicate with a service implementing the FileResolver API 031 * e.g. url's like http://some.url.dk/555-.* 032 */ 033public class FileResolverRESTClient implements FileResolver { 034 035 private static final Logger log = LoggerFactory.getLogger(FileResolverRESTClient.class); 036 private static final HttpsClientBuilder clientBuilder; 037 038 static { 039 String privateKeyFile = Settings.get(CommonSettings.FILE_RESOLVER_KEYFILE); 040 clientBuilder = new HttpsClientBuilder(privateKeyFile); 041 } 042 043 /** 044 * Base url for the API endpoint 045 */ 046 private final URL baseUrl; 047 048 public FileResolverRESTClient() { 049 baseUrl = getBaseURL(); 050 } 051 052 private URL getBaseURL() { 053 final URL baseUrl; 054 String url = Settings.get(CommonSettings.FILE_RESOLVER_BASE_URL); 055 try { 056 baseUrl = new URL(url); 057 } catch (MalformedURLException e) { 058 log.error("Malformed Url for FileResolver", e); 059 throw new RuntimeException(e); 060 } 061 return baseUrl; 062 } 063 064 @Override public List<Path> getPaths(Pattern filepattern) { 065 return getPaths(filepattern, false); 066 } 067 068 private List<Path> getPaths(Pattern filepattern, boolean exactfilename) { 069 try { 070 String pattern = filepattern.pattern(); 071 URI uri = new URL(baseUrl + "/" + URLEncoder.encode(pattern, StandardCharsets.UTF_8.toString())).toURI().normalize(); 072 CGIRequestBuilder requestBuilder = new CGIRequestBuilder(uri); 073 HttpUriRequest request = requestBuilder.buildFileResolverRequest(exactfilename); 074 CloseableHttpClient httpClient = clientBuilder.getHttpsClient(); 075 076 try (CloseableHttpResponse httpResponse = httpClient.execute(request)) { 077 InputStream istr = httpResponse.getEntity().getContent(); 078 List<String> results = IOUtils.readLines(istr); 079 return results.stream() 080 .filter(path -> !"".equals(path.trim())) //remove empties and whitespace 081 .map(pathString -> Paths.get(pathString.trim())) //convert to Path 082 .collect(Collectors.toList()); 083 } 084 } catch (IOException | URISyntaxException e) { 085 log.error("Problem resolving file " + filepattern, e); 086 return new ArrayList<>(); 087 } 088 } 089 090 /** 091 * Note that the input to this method should be a literal filename but no checking or escaping is 092 * done to prevent the inclusion of regex directives. 093 * @param filename The filename to resolve. 094 * @return The first Path to a matching file or null if no such file is found 095 */ 096 @Override public Path getPath(String filename) { 097 final List<Path> paths = getPaths(Pattern.compile(filename), true); 098 if (!paths.isEmpty()) { 099 return paths.get(0); 100 } else { 101 return null; 102 } 103 } 104}