001package com.hfg.util.io; 002 003import java.io.*; 004import java.util.Calendar; 005import java.util.Date; 006import java.util.GregorianCalendar; 007import java.net.URL; 008import java.net.HttpURLConnection; 009import java.util.logging.Level; 010import java.util.logging.Logger; 011 012import com.hfg.util.StringUtil; 013 014 015//------------------------------------------------------------------------------ 016/** 017 * Implementation of RemoteFile for HTTP files 018 * 019 * @author J. Alex Taylor, hairyfatguy.com 020 */ 021//------------------------------------------------------------------------------ 022// com.hfg XML/HTML Coding Library 023// 024// This library is free software; you can redistribute it and/or 025// modify it under the terms of the GNU Lesser General Public 026// License as published by the Free Software Foundation; either 027// version 2.1 of the License, or (at your option) any later version. 028// 029// This library is distributed in the hope that it will be useful, 030// but WITHOUT ANY WARRANTY; without even the implied warranty of 031// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 032// Lesser General Public License for more details. 033// 034// You should have received a copy of the GNU Lesser General Public 035// License along with this library; if not, write to the Free Software 036// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 037// 038// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 039// jataylor@hairyfatguy.com 040//------------------------------------------------------------------------------ 041 042 043public class HTTPRemoteFile extends AbstractRemoteFile 044{ 045 046 //########################################################################### 047 // PRIVATE FIELDS 048 //########################################################################### 049 050 private String mURL; 051 private Long mSize; 052 private Calendar mTimestamp; 053 private int mNumRedirects; 054 private String mUserAgentString = sDefaultUserAgentString; 055 private String mRequestedPath; 056 057 private static String sDefaultUserAgentString; 058 059 private static final int MAX_REDIRECTS = 10; 060 061 // Some places don't like to talk to or don't recognize Java. Pretend we're IE 062 private static final String USER_AGENT = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)"; 063 064 private final static Logger LOGGER = Logger.getLogger(HTTPRemoteFile.class.getName()); 065 066 //########################################################################### 067 // CONSTRUCTORS 068 //########################################################################### 069 070 071 //--------------------------------------------------------------------------- 072 public HTTPRemoteFile(String inURL) 073 { 074 mURL = inURL; 075 } 076 077 078 079 //########################################################################### 080 // PUBLIC METHODS 081 //########################################################################### 082 083 //--------------------------------------------------------------------------- 084 public static void setDefaultUserAgentString(String inValue) 085 { 086 sDefaultUserAgentString = inValue; 087 } 088 089 //--------------------------------------------------------------------------- 090 public static String getDefaultUserAgentString() 091 { 092 return sDefaultUserAgentString; 093 } 094 095 096 //--------------------------------------------------------------------------- 097 public HTTPRemoteFile setUserAgentString(String inValue) 098 { 099 mUserAgentString = inValue; 100 return this; 101 } 102 103 //--------------------------------------------------------------------------- 104 public String geUserAgentString() 105 { 106 return mUserAgentString; 107 } 108 109 //--------------------------------------------------------------------------- 110 public static Logger getLogger() 111 { 112 return LOGGER; 113 } 114 115 //--------------------------------------------------------------------------- 116 @Override 117 public String getURL() 118 { 119 return mURL; 120 } 121 122 //--------------------------------------------------------------------------- 123 @Override 124 public String getName() 125 { 126 String name; 127 128 int index = mURL.lastIndexOf('/'); 129 if (index > 0) 130 { 131 name = mURL.substring(index + 1); 132 } 133 else 134 { 135 name = mURL; 136 } 137 138 // Don't include arguments in the name 139 index = name.indexOf('?'); 140 if (index > 0) 141 { 142 name = name.substring(0, index); 143 } 144 145 return name; 146 } 147 148 //--------------------------------------------------------------------------- 149 @Override 150 public String getPath() 151 { 152 String path; 153 154 // Remove http:// 155 int index = mURL.indexOf("//"); 156 if (index > 0) 157 { 158 path = mURL.substring(index + 2); 159 } 160 else 161 { 162 path = mURL; 163 } 164 165 // Remove the server or server:port 166 index = path.indexOf("/"); 167 if (index > 0) 168 { 169 path = path.substring(index + 1); 170 } 171 172 // Don't include arguments in the path 173 index = path.indexOf('?'); 174 if (index > 0) 175 { 176 path = path.substring(0, index); 177 } 178 179 180 181 return path; 182 } 183 184 //--------------------------------------------------------------------------- 185 public HTTPRemoteFile setRequestedPath(String inValue) 186 { 187 mRequestedPath = inValue; 188 return this; 189 } 190 191 //--------------------------------------------------------------------------- 192 @Override 193 public String getRequestedPath() 194 { 195 return mRequestedPath != null ? mRequestedPath : getPath(); 196 } 197 198 //--------------------------------------------------------------------------- 199 @Override 200 public long getSize() 201 { 202 if (null == mSize) 203 { 204 getFileInfo(); 205 } 206 207 return mSize != null ? mSize.longValue() : -1; 208 } 209 210 //--------------------------------------------------------------------------- 211 @Override 212 public Calendar getTimestamp() 213 { 214 if (null == mTimestamp) 215 { 216 getFileInfo(); 217 } 218 219 return mTimestamp; 220 } 221 222 //--------------------------------------------------------------------------- 223 @Override 224 public InputStream getInputStream() 225 throws IOException 226 { 227 HttpURLConnection conn = (HttpURLConnection) new URL(mURL).openConnection(); 228 conn.setRequestProperty("User-Agent", USER_AGENT); 229 conn.connect(); 230 231 return new BufferedInputStream(conn.getInputStream()); 232 } 233 234 235 //--------------------------------------------------------------------------- 236 @Override 237 public boolean writeToLocalDir(File inLocalDir) 238 throws IOException 239 { 240 boolean result = super.writeToLocalDir(inLocalDir); 241 if (null == mSize 242 || mSize < 0) 243 { 244 mSize = new File(inLocalDir, getName()).length(); 245 } 246 247 return result; 248 } 249 250 //--------------------------------------------------------------------------- 251 @Override 252 public boolean copyLocallyPreservingPath(File inLocalDir) 253 throws IOException 254 { 255 boolean result = super.copyLocallyPreservingPath(inLocalDir); 256 if (null == mSize 257 || mSize < 0) 258 { 259 mSize = new File(inLocalDir, getPath()).length(); 260 } 261 262 return result; 263 } 264 265 //--------------------------------------------------------------------------- 266 @Override 267 public long writeToStream(OutputStream stream) 268 throws IOException 269 { 270 long fileSize = super.writeToStream(stream); 271 if (null == mSize 272 || mSize < 0) 273 { 274 mSize = fileSize; 275 } 276 277 return fileSize; 278 } 279 280 //--------------------------------------------------------------------------- 281 @Override 282 public boolean writeToLocalFile(File inLocalFile) 283 throws IOException 284 { 285 boolean result = super.writeToLocalFile(inLocalFile); 286 if (null == mSize 287 || mSize < 0) 288 { 289 mSize = inLocalFile.length(); 290 } 291 292 return result; 293 } 294 295 //########################################################################### 296 // PRIVATE METHODS 297 //########################################################################### 298 299 //--------------------------------------------------------------------------- 300 private void getFileInfo() 301 { 302 HttpURLConnection conn = null; 303 try 304 { 305 conn = (HttpURLConnection) new URL(mURL).openConnection(); 306 conn.setRequestMethod("HEAD"); 307 conn.setUseCaches(false); 308 conn.setInstanceFollowRedirects(false); 309 if (StringUtil.isSet(geUserAgentString())) 310 { 311 conn.setRequestProperty("User-Agent", geUserAgentString()); 312 } 313 conn.connect(); 314 315 if (getLogger().isLoggable(Level.FINE)) 316 { 317 LOGGER.log(Level.FINE, "RESPONSE CODE: " + conn.getResponseCode()); 318 LOGGER.log(Level.FINE, "RESPONSE MSG: " + conn.getResponseMessage()); 319 LOGGER.log(Level.FINE, "CONTENT LENGTH: " + conn.getContentLength()); 320 LOGGER.log(Level.FINE, "LAST MODIFIED: " + conn.getLastModified()); 321 } 322 323 final int responseCode = conn.getResponseCode(); 324 if ( responseCode == HttpURLConnection.HTTP_MOVED_PERM 325 || responseCode == HttpURLConnection.HTTP_MOVED_TEMP 326 || responseCode == HttpURLConnection.HTTP_SEE_OTHER) 327 { 328 final String newLocation = conn.getHeaderField("Location"); 329 330 final String msg = getURL() + (responseCode == HttpURLConnection.HTTP_MOVED_PERM ? " permanently" : "") + " moved to " + newLocation; 331 LOGGER.log(Level.INFO, msg); 332 333 mNumRedirects++; 334 if (mNumRedirects > MAX_REDIRECTS) 335 { 336 throw new IOException("More than " + MAX_REDIRECTS + " redirects!"); 337 } 338 339 final URL newURL = new URL(new URL(getURL()), newLocation); 340 341 conn.disconnect(); 342 343 mURL = newURL.toString(); 344 345 getFileInfo(); // Recurse 346 } 347 else if (responseCode == HttpURLConnection.HTTP_OK) 348 { 349 mSize = new Long(conn.getContentLength()); 350 351 long lastModifiedMilliseconds = conn.getLastModified(); 352 if (lastModifiedMilliseconds > 0) 353 { 354 mTimestamp = new GregorianCalendar(); 355 mTimestamp.setTime(new Date(conn.getLastModified())); 356 } 357 } 358 } 359 catch (IOException e) 360 { 361 throw new RuntimeException(e.toString()); 362 } 363 finally 364 { 365 if (conn != null) conn.disconnect(); 366 } 367 } 368}