001/* 002 * Copyright 2023 the original author or authors. 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package de.cuioss.tools.io; 017 018import static de.cuioss.tools.base.Preconditions.checkArgument; 019 020import java.io.IOException; 021import java.io.InputStream; 022import java.net.MalformedURLException; 023import java.net.URL; 024import java.net.URLConnection; 025import java.util.Optional; 026import java.util.concurrent.TimeUnit; 027 028import de.cuioss.tools.logging.CuiLogger; 029import lombok.EqualsAndHashCode; 030import lombok.ToString; 031 032/** 033 * This {@link FileLoader} takes a {@link URL} as its parameter which is useful 034 * when e.g. iterating over an enumeration of URLs from 035 * {@link ClassLoader#getResources(String)}. It converts the given URL to a 036 * {@code String} and prefixes it with {@link FileTypePrefix#URL}. 037 * 038 * @author Sven Haag 039 */ 040@EqualsAndHashCode(of = { "url" }) 041@ToString(of = { "url" }) 042public class UrlLoader implements FileLoader { 043 044 private static final long serialVersionUID = -8758614099334823819L; 045 046 private static final CuiLogger log = new CuiLogger(UrlLoader.class); 047 048 private final URL url; 049 private transient URLConnection connection; 050 051 /** 052 * @param url representing a valid URL 053 * @throws IllegalArgumentException indicating that the given String represents 054 * a valid URL 055 */ 056 public UrlLoader(final String url) { 057 var sanitizedUrl = url; 058 if (FileTypePrefix.URL.is(url)) { 059 sanitizedUrl = FileTypePrefix.URL.removePrefix(url); 060 } 061 062 try { 063 this.url = new URL(sanitizedUrl); 064 } catch (final MalformedURLException e) { 065 throw new IllegalArgumentException("Provided URL is invalid: " + url, e); 066 } 067 } 068 069 /** 070 * @param url hopefully a JAR URL 071 */ 072 public UrlLoader(final URL url) { 073 checkArgument(null != url, "url must not be null"); 074 this.url = url; 075 } 076 077 /** 078 * @return true, if a connection to {@link #getURL()} can be established 079 */ 080 @Override 081 public boolean isReadable() { 082 try { 083 var con = getConnection(); 084 if (con.isPresent()) { 085 con.get().connect(); 086 return true; 087 } 088 } catch (final IOException e) { 089 log.error(e, "Could not read from URL: {}", getURL()); 090 } 091 return false; 092 } 093 094 @Override 095 public StructuredFilename getFileName() { 096 return new StructuredFilename(getURL().getPath()); 097 } 098 099 @Override 100 public InputStream inputStream() throws IOException { 101 var con = getConnection(); 102 if (con.isPresent()) { 103 return con.get().getInputStream(); 104 } 105 return null; 106 } 107 108 @Override 109 public URL getURL() { 110 return url; 111 } 112 113 @Override 114 public boolean isFilesystemLoader() { 115 return false; 116 } 117 118 private Optional<URLConnection> getConnection() { 119 if (null == connection) { 120 try { 121 connection = url.openConnection(); 122 connection.setConnectTimeout((int) TimeUnit.SECONDS.toMillis(5)); 123 connection.setReadTimeout((int) TimeUnit.SECONDS.toMillis(5)); 124 } catch (final IOException e) { 125 log.error(e, "Portal-538: Could not read from URL: {}", getURL()); 126 } 127 } 128 return Optional.ofNullable(connection); 129 } 130}