org.apache.wicket.core.util.resource.UrlResourceStream.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wicket.core.util.resource.UrlResourceStream.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.wicket.core.util.resource;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import org.apache.wicket.Application;
import org.apache.wicket.util.io.Connections;
import org.apache.wicket.util.io.IOUtils;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.lang.Bytes;
import org.apache.wicket.util.lang.Objects;
import org.apache.wicket.util.resource.AbstractResourceStream;
import org.apache.wicket.util.resource.IFixedLocationResourceStream;
import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
import org.apache.wicket.util.time.Time;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * UrlResourceStream implements IResource for URLs.
 *
 * @see org.apache.wicket.util.resource.IResourceStream
 * @see org.apache.wicket.util.watch.IModifiable
 * @author Jonathan Locke
 * @author Igor Vaynberg
 */
public class UrlResourceStream extends AbstractResourceStream implements IFixedLocationResourceStream {
    private static final long serialVersionUID = 1L;

    /** Logging. */
    private static final Logger log = LoggerFactory.getLogger(UrlResourceStream.class);

    /**
     * The meta data for this stream. Lazy loaded on demand.
     */
    private transient StreamData streamData;

    /** The URL to this resource. */
    private final URL url;

    /** Last known time the stream was last modified. */
    private Time lastModified;

    /**
     * Meta data class for the stream attributes
     */
    private static class StreamData {
        private URLConnection connection;

        /**
         * The streams read from this connection.
         * Some URLConnection implementations return the same instance of InputStream
         * every time URLConnection#getInputStream() is called. Other return a new instance
         * of InputStream.
         * Here we keep a list of all returned ones and close them in UrlResourceStream#close().
         * Even it is the same instance several times we will try to close it quietly several times.
         */
        private List<InputStream> inputStreams;

        /** Length of stream. */
        private long contentLength;

        /** Content type for stream. */
        private String contentType;

    }

    /**
     * Construct.
     *
     * @param url
     *            URL of resource
     */
    public UrlResourceStream(final URL url) {
        this.url = Args.notNull(url, "url");
    }

    /**
     * Lazy loads the stream settings on demand
     *
     * @param initialize
     *            a flag indicating whether to load the settings
     * @return the meta data with the stream settings
     */
    private StreamData getData(boolean initialize) {
        if (streamData == null && initialize) {
            streamData = new StreamData();

            try {
                streamData.connection = url.openConnection();
                streamData.contentLength = streamData.connection.getContentLength();

                if (Application.exists()) {
                    streamData.contentType = Application.get().getMimeType(url.getFile());
                } else {
                    streamData.contentType = streamData.connection.getContentType();
                }

                if (streamData.contentType == null || streamData.contentType.contains("unknown")) {
                    streamData.contentType = URLConnection.getFileNameMap().getContentTypeFor(url.getFile());
                }
            } catch (IOException ex) {
                throw new IllegalArgumentException("Invalid URL parameter " + url, ex);
            }
        }

        return streamData;
    }

    /**
     * Closes this resource.
     *
     * @throws IOException
     */
    @Override
    public void close() throws IOException {
        StreamData data = getData(false);

        if (data != null) {
            Connections.closeQuietly(data.connection);
            if (data.inputStreams != null) {
                for (InputStream is : data.inputStreams) {
                    IOUtils.closeQuietly(is);
                }
            }
            streamData = null;
        }
    }

    /**
     * @return A readable input stream for this resource.
     * @throws ResourceStreamNotFoundException
     */
    @Override
    public InputStream getInputStream() throws ResourceStreamNotFoundException {
        try {
            StreamData data = getData(true);
            InputStream is = data.connection.getInputStream();
            if (data.inputStreams == null) {
                data.inputStreams = new ArrayList<>();
            }
            data.inputStreams.add(is);
            return is;
        } catch (IOException e) {
            throw new ResourceStreamNotFoundException("Resource " + url + " could not be opened", e);
        }
    }

    /**
     * @return The URL to this resource (if any)
     */
    public URL getURL() {
        return url;
    }

    /**
     * @see org.apache.wicket.util.watch.IModifiable#lastModifiedTime()
     * @return The last time this resource was modified
     */
    @Override
    public Time lastModifiedTime() {
        try {
            // get url modification timestamp
            final Time time = Connections.getLastModified(url);

            // if timestamp changed: update content length and last modified date
            if (Objects.equal(time, lastModified) == false) {
                lastModified = time;
                updateContentLength();
            }
            return lastModified;
        } catch (IOException e) {
            log.warn("getLastModified() for '{}' failed: {}", url, e.getMessage());

            // allow modification watcher to detect the problem
            return null;
        }
    }

    private void updateContentLength() throws IOException {
        StreamData data = getData(false);

        if (data != null) {
            URLConnection connection = url.openConnection();
            try {
                data.contentLength = connection.getContentLength();
            } finally {
                Connections.close(connection);
            }
        }
    }

    @Override
    public String toString() {
        return url.toString();
    }

    /**
     * @return The content type of this resource, such as "image/jpeg" or "text/html"
     */
    @Override
    public String getContentType() {
        return getData(true).contentType;
    }

    @Override
    public Bytes length() {
        long length = getData(true).contentLength;

        if (length == -1) {
            return null;
        }

        return Bytes.bytes(length);
    }

    @Override
    public String locationAsString() {
        return url.toExternalForm();
    }
}