001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.util.resource;
018
019import java.io.IOException;
020import java.io.InputStream;
021import java.net.URLConnection;
022import java.nio.file.Files;
023import java.nio.file.Path;
024import java.nio.file.attribute.BasicFileAttributes;
025import java.nio.file.attribute.FileTime;
026import java.time.Instant;
027import org.apache.wicket.util.file.File;
028import org.apache.wicket.util.lang.Args;
029import org.apache.wicket.util.lang.Bytes;
030
031/**
032 * A FileSystemResourceStream is an IResourceStream implementation for Java NIO paths.
033 * 
034 * @see org.apache.wicket.util.resource.IResourceStream
035 * @see org.apache.wicket.util.watch.IModifiable
036 * @author Tobias Soloschenko
037 */
038public class FileSystemResourceStream extends AbstractResourceStream
039        implements
040                IFixedLocationResourceStream
041{
042        private static final long serialVersionUID = 1L;
043
044        /** Any associated path */
045        private final Path path;
046
047        /** Resource stream */
048        private transient InputStream inputStream;
049
050        /**
051         * Constructor.
052         * 
053         * @param path
054         *            {@link Path} containing resource
055         */
056        public FileSystemResourceStream(final Path path)
057        {
058                Args.notNull(path, "path");
059                this.path = path;
060        }
061
062        /**
063         * Constructor.
064         * 
065         * @param file
066         *            {@link java.io.File} containing resource
067         */
068        public FileSystemResourceStream(final java.io.File file)
069        {
070                Args.notNull(file, "file");
071                this.path = file.toPath();
072        }
073
074        /**
075         * Constructor.
076         * 
077         * @param file
078         *            {@link File} containing resource
079         */
080        public FileSystemResourceStream(final File file)
081        {
082                Args.notNull(file, "file");
083                this.path = file.toPath();
084        }
085
086        @Override
087        public InputStream getInputStream() throws ResourceStreamNotFoundException
088        {
089                if (inputStream == null)
090                {
091                        try
092                        {
093                                inputStream = Files.newInputStream(path);
094                        }
095                        catch (IOException e)
096                        {
097                                throw new ResourceStreamNotFoundException("Input stream of path " + path +
098                                        " could not be acquired", e);
099                        }
100                }
101                return inputStream;
102        }
103
104        @Override
105        public void close() throws IOException
106        {
107                if (inputStream != null)
108                {
109                        inputStream.close();
110                        inputStream = null;
111                }
112        }
113
114        @Override
115        public String getContentType()
116        {
117                try
118                {
119                        String contentType = Files.probeContentType(path);
120                        if (contentType == null)
121                        {
122                                contentType = URLConnection.getFileNameMap().getContentTypeFor(
123                                        path.getFileName().toString());
124                        }
125                        return contentType;
126                }
127                catch (IOException e)
128                {
129                        throw new RuntimeException("Content type of path " + path + " could not be acquired", e);
130                }
131        }
132
133        /**
134         * @return The path this resource resides in, if any.
135         */
136        public final Path getPath()
137        {
138                return path;
139        }
140
141        @Override
142        public Instant lastModifiedTime()
143        {
144                try
145                {
146                        BasicFileAttributes attributes = Files.readAttributes(path, BasicFileAttributes.class);
147                        FileTime lastModifiedTime = attributes.lastModifiedTime();
148                        long millis = lastModifiedTime.toMillis();
149                        return Instant.ofEpochMilli(millis);
150                }
151                catch (IOException e)
152                {
153                        throw new RuntimeException("Modification time of path " + path +
154                                " could not be acquired", e);
155                }
156        }
157
158        @Override
159        public Bytes length()
160        {
161                try
162                {
163                        BasicFileAttributes attributes = Files.readAttributes(path, BasicFileAttributes.class);
164                        long size = attributes.size();
165                        return Bytes.bytes(size);
166                }
167                catch (IOException e)
168                {
169                        throw new RuntimeException("Length of path " + path + " could not be acquired", e);
170                }
171        }
172
173        @Override
174        public String locationAsString()
175        {
176                return path.toString();
177        }
178
179        @Override
180        public String toString()
181        {
182                return locationAsString();
183        }
184}