jaulib v1.3.0
Jau Support Library (C++, Java, ..)
AssetURLContext.java
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2021 Gothel Software e.K.
4 * Copyright (c) 2012 Gothel Software e.K.
5 * Copyright (c) 2013 JogAmp Community.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26package org.jau.net;
27
28import java.io.File;
29import java.io.FileNotFoundException;
30import java.io.IOException;
31import java.net.MalformedURLException;
32import java.net.URISyntaxException;
33import java.net.URL;
34import java.net.URLConnection;
35import java.net.URLStreamHandler;
36
37import org.jau.io.IOUtil;
38import org.jau.lang.ExceptionUtils;
39import org.jau.sys.AndroidVersion;
40
41/**
42 * See {@link PiggybackURLConnection} for description and examples.
43 */
44public abstract class AssetURLContext implements PiggybackURLContext {
45 private static final boolean DEBUG = IOUtil.DEBUG;
46
47 /** The <i>asset URL</i> protocol name <code>asset</code> */
48 public static final String asset_protocol = "asset";
49
50 /** The <i>asset URL</i> protocol prefix <code>asset:</code> */
51 public static final String asset_protocol_prefix = "asset:";
52
53 /**
54 * The <i>optional</i> <i>asset</i> folder name with ending slash <code>assets/</code>.
55 * <p>
56 * Note that the <i>asset</i> folder is not used on all platforms using the <i>asset</i> protocol
57 * and you should not rely on it, use {@link AssetURLConnection#getEntryName()}.
58 * </p>
59 **/
60 public static final String assets_folder = "assets/";
61
62 public static AssetURLContext create(final ClassLoader cl) {
63 return new AssetURLContext() {
64 @Override
65 public ClassLoader getClassLoader() {
66 return cl;
67 }
68 };
69 }
70
71 public static AssetURLStreamHandler createHandler(final ClassLoader cl) {
72 return new AssetURLStreamHandler(create(cl));
73 }
74
75 /**
76 * Create an <i>asset</i> URL, suitable even w/o the registered <i>asset</i> URLStreamHandler.
77 * <p>
78 * This is equivalent with:
79 * <pre>
80 * return new URL(null, path.startsWith("asset:") ? path : "asset:" + path, new AssetURLStreamHandler(cl));
81 * </pre>
82 * </p>
83 * @param path resource path, with or w/o <code>asset:</code> prefix
84 * @param cl the ClassLoader used to resolve the location, see {@link #getClassLoader()}.
85 * @return
86 * @throws MalformedURLException
87 */
88 public static URL createURL(final String path, final ClassLoader cl) throws MalformedURLException {
89 return new URL(null, path.startsWith(asset_protocol_prefix) ? path : asset_protocol_prefix + path, createHandler(cl));
90 }
91
92 /**
93 * Create an <i>asset</i> URL, suitable only with the registered <i>asset</i> URLStreamHandler.
94 * <p>
95 * This is equivalent with:
96 * <pre>
97 * return new URL(path.startsWith("asset:") ? path : "asset:" + path);
98 * </pre>
99 * </p>
100 * @param path resource path, with or w/o <code>asset:</code> prefix
101 * @return
102 * @throws MalformedURLException
103 */
104 public static URL createURL(final String path) throws MalformedURLException {
105 return new URL(path.startsWith(asset_protocol_prefix) ? path : asset_protocol_prefix + path);
106 }
107
108 /**
109 * Returns the <i>asset</i> handler previously set via {@link #registerHandler(ClassLoader)},
110 * or null if none was set.
111 */
112 public static URLStreamHandler getRegisteredHandler() {
114 return ( null != f ) ? f.getHandler(asset_protocol) : null;
115 }
116
117 /**
118 * Registers the generic URLStreamHandlerFactory via {@link GenericURLStreamHandlerFactory#register()}
119 * and if successful sets the <i>asset</i> <code>handler</code> for the given ClassLoader <code>cl</code>.
120 *
121 * @return true if successful, otherwise false
122 */
123 public static boolean registerHandler(final ClassLoader cl) {
125 if( null != f ) {
127 return true;
128 } else {
129 return false;
130 }
131 }
132
133 /**
134 * Returns an <i>asset</i> aware ClassLoader.
135 * <p>
136 * The ClassLoader is required to find the <i>asset</i> resource
137 * via it's <code>URL findResource(String)</code> implementation.
138 * </p>
139 * <p>
140 * It's <code>URL findResource(String)</code> implementation shall return either
141 * an <i>asset</i> URL <code>asset:sub-protocol</code> or just the sub-protocol URL.
142 * </p>
143 * <p>
144 * For example, on Android, we <i>redirect</i> all <code>path</code> request to <i>assets/</i><code>path</code>.
145 * </p>
146 */
147 public abstract ClassLoader getClassLoader();
148
149 @Override
150 public String getImplementedProtocol() {
151 return asset_protocol;
152 }
153
154 /**
155 * {@inheritDoc}
156 * <p>
157 * This implementation attempts to resolve <code>path</code> in the following order:
158 * <ol>
159 * <li> as a valid URL: <code>new URL(path)</code>, use sub-protocol if <i>asset</i> URL</li>
160 * <li> via ClassLoader: {@link #getClassLoader()}.{@link ClassLoader#getResource(String) getResource(path)}, use sub-protocol if <i>asset</i> URL </li>
161 * <li> as a File: <code>new File(path).toURI().toURL()</code>
162 * </ol>
163 * </p>
164 * <p>
165 * In case of using the ClassLoader (2) <b>and</b> if running on Android,
166 * the {@link #assets_folder} is being prepended to <code>path</code> if missing.
167 * </p>
168 **/
169 @Override
170 public URLConnection resolve(final String path) throws IOException {
171 return resolve(path, getClassLoader());
172 }
173
174 public static URLConnection resolve(String path, final ClassLoader cl) throws IOException {
175 URL url = null;
176 URLConnection conn = null;
177 int type = -1;
178
179 if(DEBUG) {
180 System.err.println("AssetURLContext.resolve: <"+path+">");
181 }
182 try {
183 path = IOUtil.cleanPathString(path);
184 } catch (final URISyntaxException uriEx) {
185 throw new IOException(uriEx);
186 }
187
188 try {
189 // lookup as valid sub-protocol
190 url = new URL(path);
191 conn = open(url);
192 type = null != conn ? 1 : -1;
193 } catch(final MalformedURLException e1) { if(DEBUG) { System.err.println("FAIL(1): "+e1.getMessage()); } }
194
195 if(null == conn && null != cl) {
196 // lookup via ClassLoader .. cleanup leading '/'
197 String cpath = path;
198 while(cpath.startsWith("/")) {
199 cpath = cpath.substring(1);
200 }
202 cpath = cpath.startsWith(assets_folder) ? cpath : assets_folder + cpath;
203 }
204 url = cl.getResource(cpath);
205 conn = open(url);
206 type = null != conn ? 2 : -1;
207 }
208
209 if(null == conn) {
210 // lookup as File
211 try {
212 final File file = new File(path);
213 if(file.exists()) {
214 url = Uri.valueOf(file).toURL();
215 conn = open(url);
216 type = null != conn ? 3 : -1;
217 }
218 } catch (final Throwable e) { if(DEBUG) { System.err.println("FAIL(3): "+e.getMessage()); } }
219 }
220
221 if(DEBUG) {
222 System.err.println("AssetURLContext.resolve: type "+type+": url <"+url+">, conn <"+conn+">, connURL <"+(null!=conn?conn.getURL():null)+">");
223 }
224 if(null == conn) {
225 throw new FileNotFoundException("Could not look-up: "+path+" as URL, w/ ClassLoader or as File");
226 }
227 return conn;
228 }
229
230 private static URLConnection open(final URL url) {
231 if(null==url) {
232 return null;
233 }
234 try {
235 final URLConnection c = url.openConnection();
236 c.connect(); // redundant
237 return c;
238 } catch (final IOException ioe) { if(DEBUG) { System.err.println("FAIL(2): "+ioe.getMessage()); } }
239 return null;
240 }
241
242 /**
243 * Locating a resource using the ClassLoader's facilities.
244 * <p>
245 * Returns the resolved and connected URLConnection or null if not found.
246 * </p>
247 *
248 * @see ClassLoader#getResource(String)
249 * @see ClassLoader#getSystemResource(String)
250 * @see URL#URL(String)
251 * @see File#File(String)
252 */
253 public static URLConnection getResource(final String resourcePath, final ClassLoader cl) {
254 if(null == resourcePath) {
255 return null;
256 }
257 if(DEBUG) {
258 System.err.println("AssetURLContext: locating <"+resourcePath+">, has cl: "+(null!=cl));
259 }
260 if(resourcePath.startsWith(AssetURLContext.asset_protocol_prefix)) {
261 try {
262 return AssetURLContext.createURL(resourcePath, cl).openConnection();
263 } catch (final IOException ioe) {
264 if(DEBUG) {
265 ExceptionUtils.dumpThrowable("AssetURLContext", ioe);
266 }
267 return null;
268 }
269 } else {
270 try {
271 return AssetURLContext.resolve(resourcePath, cl);
272 } catch (final IOException ioe) {
273 if(DEBUG) {
274 ExceptionUtils.dumpThrowable("AssetURLContext", ioe);
275 }
276 }
277 }
278 return null;
279 }
280}
static final boolean DEBUG
Definition: IOUtil.java:62
static String cleanPathString(String path)
Definition: IOUtil.java:629
static void dumpThrowable(final String additionalDescr, final Throwable t)
Dumps a Throwable to System.err in a decorating message including the current thread name,...
See PiggybackURLConnection for description and examples.
static URL createURL(final String path)
Create an asset URL, suitable only with the registered asset URLStreamHandler.
static final String asset_protocol_prefix
The asset URL protocol prefix asset:
URLConnection resolve(final String path)
Resolving path to a URL sub protocol and return it's open URLConnection.
static boolean registerHandler(final ClassLoader cl)
Registers the generic URLStreamHandlerFactory via GenericURLStreamHandlerFactory#register() and if su...
String getImplementedProtocol()
Returns the specific protocol, constant for this implementation.
static final String assets_folder
The optional asset folder name with ending slash assets/.
static URLConnection getResource(final String resourcePath, final ClassLoader cl)
Locating a resource using the ClassLoader's facilities.
abstract ClassLoader getClassLoader()
Returns an asset aware ClassLoader.
static AssetURLStreamHandler createHandler(final ClassLoader cl)
static URLStreamHandler getRegisteredHandler()
Returns the asset handler previously set via registerHandler(ClassLoader), or null if none was set.
static URLConnection resolve(String path, final ClassLoader cl)
static final String asset_protocol
The asset URL protocol name asset
static URL createURL(final String path, final ClassLoader cl)
Create an asset URL, suitable even w/o the registered asset URLStreamHandler.
static AssetURLContext create(final ClassLoader cl)
URLStreamHandler to handle the asset protocol.
static synchronized GenericURLStreamHandlerFactory register()
Returns the singleton instance of the registered GenericURLStreamHandlerFactory or null if registrati...
synchronized final URLStreamHandler setHandler(final String protocol, final URLStreamHandler handler)
Sets the handler for protocol.
synchronized final URLStreamHandler getHandler(final String protocol)
Returns the protocol handler previously set via setHandler(String, URLStreamHandler),...
This class implements an immutable Uri as defined by RFC 2396.
Definition: Uri.java:162
final java.net.URL toURL()
Returns a new URL instance using the encoded input string, new URL(uri.input), i.e.
Definition: Uri.java:1331
static Uri valueOf(final File file)
Creates a new Uri instance using the given File instance.
Definition: Uri.java:1126
static final boolean isAvailable
See PiggybackURLConnection for description and examples.