com.wavemaker.commons.io.ResourceURL Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (C) 2022-2023 WaveMaker, Inc.
*
* 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.
******************************************************************************/
package com.wavemaker.commons.io;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.wavemaker.commons.io.exception.ResourceTypeMismatchException;
/**
* Factory class that can be used to construct a {@link URL} for a given {@link Resource}. The {@link URL}s returned
* from this class can be used to access file content using the {@link URL#openStream()} method, however, the URL cannot
* be serialized. URLs can be used with a {@link URLClassLoader}.
*
* @author Phillip Webb
*/
public abstract class ResourceURL {
public static final String PROTOCOL = "rfs";
private ResourceURL() {
}
/**
* Get a URL for the given {@link Resource}.
*
* @param resource the resource
*
* @return a URL for the resource
*/
public static URL get(Resource resource) throws MalformedURLException {
return get(resource, false);
}
/**
* Get a URL for the given {@link Resource}.
*
* @param resource the resource
* @param nonLocking if the URL should protect against file locking
*
* @return a URL for the resource
*/
public static URL get(Resource resource, boolean nonLocking) throws MalformedURLException {
String spec = getSpec(resource);
ResourceURLStreamHandler handler = new ResourceURLStreamHandler(resource, nonLocking);
return new URL(null, spec, handler);
}
/**
* Get a {@link List} of {@link URL}s for the given {@link Resource}s.
*
* @return a list of URLs for the resource
*/
public static List getForResources(Iterable extends Resource> resources) throws MalformedURLException {
return getForResources(resources, false);
}
/**
* Get a {@link List} of {@link URL}s for the given {@link Resource}s.
*
* @param nonLocking if the URL should protect against file locking
*
* @return a list of URLs for the resource
*/
public static List getForResources(Iterable extends Resource> resources, boolean nonLocking) throws MalformedURLException {
List urls = new ArrayList<>();
for (Resource resource : resources) {
urls.add(get(resource, nonLocking));
}
return Collections.unmodifiableList(urls);
}
private static String getSpec(Resource resource) {
return PROTOCOL + ":" + resource;
}
/**
* Internal {@link URLStreamHandler} used with {@link Resource} URLs.
*/
private static class ResourceURLStreamHandler extends URLStreamHandler {
private final Folder root;
private final boolean nonLocking;
public ResourceURLStreamHandler(Resource resource, boolean nonLocking) {
this.root = findRoot(resource);
this.nonLocking = nonLocking;
}
@Override
protected void parseURL(URL u, String spec, int start, int limit) {
super.parseURL(u, spec, start, limit);
}
private Folder findRoot(Resource resource) {
Resource rootResource = resource;
while (rootResource.getParent() != null) {
rootResource = rootResource.getParent();
}
return (Folder) rootResource;
}
@Override
protected URLConnection openConnection(URL url) throws IOException {
String path = url.getPath();
try {
File file = this.root.getFile(path);
if (!file.exists()) {
throw new IOException("File '" + file + "' does not exist");
}
return new FileURLConnection(url, file, this.nonLocking);
} catch (ResourceTypeMismatchException e) {
throw new IOException("Unable to open URL connection to folder '" + path + "'", e);
}
}
}
/**
* Internal {@link URLConnection} used with {@link Resource} URLs.
*/
private static class FileURLConnection extends URLConnection {
private final File file;
private final boolean nonLocking;
public FileURLConnection(URL url, File file, boolean nonLocking) {
super(url);
this.file = file;
this.nonLocking = nonLocking;
}
@Override
public void connect() throws IOException {
}
@Override
public InputStream getInputStream() throws IOException {
if (this.nonLocking) {
return new ByteArrayInputStream(this.file.getContent().asBytes());
}
return this.file.getContent().asInputStream();
}
}
}