All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sirius.web.resources.Resource Maven / Gradle / Ivy

/*
 * Made with all the love in the world
 * by scireum in Remshalden, Germany
 *
 * Copyright by scireum GmbH
 * http://www.scireum.de - [email protected]
 */

package sirius.web.resources;

import com.google.common.base.Charsets;
import com.google.common.io.ByteStreams;
import sirius.kernel.Sirius;
import sirius.kernel.commons.RateLimit;
import sirius.kernel.commons.Strings;
import sirius.kernel.health.Exceptions;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * Wraps an resolved URL as resource.
 * 

* Used as result of a {@link Resolver}. Next to the resolved URL it also stores the original * scope and path which was used to resolve the url. * * @see Resolver * @see sirius.web.security.UserContext#getCurrentScope() */ public class Resource { private static final String PROTOCOL_FILE = "file"; private final String scopeId; private final String path; private final URL url; private final File file; private long lastModified = -1; private final boolean consideredConstant; private final RateLimit checkInterval = RateLimit.timeInterval(10, TimeUnit.SECONDS); private long minLastModified; private Resource(String scopeId, String path, URL url, boolean constant) { this.scopeId = scopeId; this.path = path; this.url = url; this.file = determineFile(url); if (file != null && (Sirius.isDev() || Sirius.isStartedAsTest())) { ensureCaseMatch(path); } this.consideredConstant = constant; this.minLastModified = System.currentTimeMillis(); Objects.requireNonNull(scopeId); Objects.requireNonNull(path); Objects.requireNonNull(url); } private void ensureCaseMatch(String path) { try { String absolutePath = file.getAbsolutePath(); String canonicalPath = file.getCanonicalPath(); if (!absolutePath.equals(canonicalPath) && absolutePath.equalsIgnoreCase(canonicalPath)) { throw new IllegalStateException(Strings.apply( "A resource was found, but only case insensitive: %s vs. %s", path, canonicalPath)); } } catch (IOException e) { Exceptions.ignore(e); } } private File determineFile(URL url) { if (!PROTOCOL_FILE.equals(url.getProtocol())) { return null; } try { File localFile = new File(url.toURI()); if (localFile.exists()) { return localFile; } else { return null; } } catch (URISyntaxException e) { Exceptions.ignore(e); return null; } } /** * Creates a new dynamic resource for the given scope, path and resulting url. *

* A dynamic resource might change over time (like a customer specific file on disk). Therefore we * frequently check for changes. * * @param scopeId the scope which was used to resolve the resource * @param path the local path pointing to the resource * @param url the url pointing to the actual content * @return a new resource based on the given parameters */ public static Resource dynamicResource(String scopeId, String path, URL url) { return new Resource(scopeId, path, url, false); } /** * Creates a new constant resource for the given scope, path and resulting url. *

* A constant resource will not change over time. An example might be a file in the classpath. Note that in * debug mode ({@link sirius.kernel.Sirius#isDev()}) all resources are considered dynamic. * * @param scopeId the scope which was used to resolve the resource * @param path the local path pointing to the resource * @param url the url pointing to the actual content * @return a new resource based on the given parameters */ public static Resource constantResource(String scopeId, String path, URL url) { return new Resource(scopeId, path, url, true); } /** * Returns the scope id which was set when resolving the resource. * * @return the scope id for which the resource was resolved */ public String getScopeId() { return scopeId; } /** * Returns the path which was used to lookup the resource. * * @return the path used to resolve the resource */ public String getPath() { return path; } /** * Returns the effective URL pointing to the resolved content * * @return the URL pointing to the content */ public URL getUrl() { return url; } /** * Returns the contents of the resource as {@link java.io.InputStream}. * * @return an input stream containing the contents of the resource */ public InputStream openStream() { try { if (file != null) { return new FileInputStream(file); } return getUrl().openConnection().getInputStream(); } catch (IOException e) { throw Exceptions.handle(e); } } /** * Returns the contents of the resource as byte array. * * @return the contents of the resource */ public byte[] getContent() { try { try (InputStream in = openStream()) { return ByteStreams.toByteArray(in); } } catch (IOException e) { throw Exceptions.handle(e); } } /** * Returns the contents of the resource as string. * * @return the contents of the resource as string (expecting UTF-8 as encoding). */ public String getContentAsString() { return new String(getContent(), Charsets.UTF_8); } /** * Computes the last modified date. *

* This value will be cached if the resource is considered constant. However, in development systems * the resource will sill be checked in a regular interval. *

* Also note that the last modified timestamp is * at least the timestamp when the resource was resolved. This is required to manage dynamic resources which * suddenly are created or vanish. * * @return the last modified value of the underlying resource. */ public long getLastModified() { if (lastModified == -1 || ((!consideredConstant || Sirius.isDev()) && checkInterval.check())) { try { if (file != null) { lastModified = file.lastModified(); } else { determineLastModified(); } } catch (IOException e) { Exceptions.handle(e); lastModified = System.currentTimeMillis(); } } return Math.max(lastModified, minLastModified); } private void determineLastModified() throws IOException { URLConnection c = url.openConnection(); lastModified = c.getLastModified(); try { // Close the input stream since the stupid implementation of // SUN's FileURLConnection always keeps an InputStream open on // connect. c.getInputStream().close(); } catch (Exception e) { Resources.LOG.WARN(e); } } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj == null) { return false; } if (!(obj instanceof Resource)) { return false; } return Objects.equals(path, ((Resource) obj).path) && Objects.equals(scopeId, ((Resource) obj).scopeId); } @Override public int hashCode() { return Objects.hash(path, scopeId); } @Override public String toString() { return Strings.apply("%s (%s): %s", path, scopeId, url); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy