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}