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.io;
018
019import java.io.File;
020import java.io.IOException;
021import java.io.InputStream;
022import java.net.HttpURLConnection;
023import java.net.JarURLConnection;
024import java.net.URL;
025import java.net.URLConnection;
026import java.time.Instant;
027import org.apache.wicket.util.file.Files;
028
029/**
030 * {@link URLConnection} related utilities
031 * 
032 * @author igor.vaynberg
033 */
034public class Connections
035{
036        private Connections()
037        {
038        }
039
040        /**
041         * Gets last modified date of the given {@link URL}
042         * 
043         * @param url
044         * @return last modified timestamp or <code>null</code> if not available
045         * @throws IOException
046         */
047        public static Instant getLastModified(final URL url) throws IOException
048        {
049                // check if url points to a local file
050                final File file = Files.getLocalFileFromUrl(url);
051
052                if (file != null)
053                {
054                        // in that case we can get the timestamp faster
055                        return Files.getLastModified(file);
056                }
057
058                // otherwise open the url and proceed
059                URLConnection connection = url.openConnection();
060
061                final long milliseconds;
062
063                try
064                {
065                        if (connection instanceof JarURLConnection)
066                        {
067                                JarURLConnection jarUrlConnection = (JarURLConnection)connection;
068                                URL jarFileUrl = jarUrlConnection.getJarFileURL();
069                                URLConnection jarFileConnection = jarFileUrl.openConnection();
070                                jarFileConnection.setDoInput(false);
071                                // get timestamp from JAR
072                                milliseconds = jarFileConnection.getLastModified();
073                        }
074                        else
075                        {
076                                // get timestamp from URL
077                                milliseconds = connection.getLastModified();
078                        }
079
080                        // return null if timestamp is unavailable
081                        if (milliseconds == 0)
082                        {
083                                return null;
084                        }
085
086                        // return UNIX timestamp
087                        return Instant.ofEpochMilli(milliseconds);
088
089                }
090                finally
091                {
092                        closeQuietly(connection);
093                }
094        }
095
096        /**
097         * Closes a connection, ignoring any exceptions if they occur
098         * 
099         * @param connection
100         */
101        public static void closeQuietly(final URLConnection connection)
102        {
103                try
104                {
105                        close(connection);
106                }
107                catch (Exception e)
108                {
109                        // ignore
110                }
111        }
112
113        /**
114         * Closes a connection
115         * 
116         * @param connection
117         * @throws IOException
118         */
119        public static void close(final URLConnection connection) throws IOException
120        {
121                if (connection == null)
122                {
123                        return;
124                }
125
126                String protocol = connection.getURL().getProtocol();
127                if ("file".equals(protocol)) // XXX what about JarUrlConnection ?!
128                {
129                        // Close FileUrlConnection's input stream because
130                        // otherwise it leaks open file handles. See WICKET-4359.
131                        // Most other connection types should not call getInputStream() here,
132                        // especially remote connections.
133                        InputStream inputStream = connection.getInputStream();
134
135                        if (inputStream != null)
136                        {
137                                inputStream.close();
138                        }
139                }
140
141                if (connection instanceof HttpURLConnection)
142                {
143                        ((HttpURLConnection)connection).disconnect();
144                }
145        }
146}