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: *
017: */
018:
019: package org.apache.tools.ant.types.resources;
020:
021: import java.io.File;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.io.OutputStream;
025: import java.net.HttpURLConnection;
026: import java.net.URL;
027: import java.net.URLConnection;
028: import java.net.MalformedURLException;
029: import java.net.JarURLConnection;
030: import java.util.jar.JarFile;
031:
032: import org.apache.tools.ant.Project;
033: import org.apache.tools.ant.BuildException;
034: import org.apache.tools.ant.types.Resource;
035: import org.apache.tools.ant.types.Reference;
036: import org.apache.tools.ant.util.FileUtils;
037:
038: /**
039: * Exposes a URL as a Resource.
040: * @since Ant 1.7
041: */
042: public class URLResource extends Resource {
043: private static final FileUtils FILE_UTILS = FileUtils
044: .getFileUtils();
045: private static final int NULL_URL = Resource
046: .getMagicNumber("null URL".getBytes());
047:
048: private URL url;
049: private URLConnection conn;
050:
051: /**
052: * Default constructor.
053: */
054: public URLResource() {
055: }
056:
057: /**
058: * Convenience constructor.
059: * @param u the URL to expose.
060: */
061: public URLResource(URL u) {
062: setURL(u);
063: }
064:
065: /**
066: * Convenience constructor.
067: * @param f the File to set as a URL.
068: */
069: public URLResource(File f) {
070: setFile(f);
071: }
072:
073: /**
074: * String constructor for Ant attribute introspection.
075: * @param u String representation of this URL.
076: * @see org.apache.tools.ant.IntrospectionHelper
077: */
078: public URLResource(String u) {
079: this (newURL(u));
080: }
081:
082: /**
083: * Set the URL for this URLResource.
084: * @param u the URL to expose.
085: */
086: public synchronized void setURL(URL u) {
087: checkAttributesAllowed();
088: url = u;
089: }
090:
091: /**
092: * Set the URL from a File.
093: * @param f the File to set as a URL.
094: */
095: public synchronized void setFile(File f) {
096: try {
097: setURL(FILE_UTILS.getFileURL(f));
098: } catch (MalformedURLException e) {
099: throw new BuildException(e);
100: }
101: }
102:
103: /**
104: * Get the URL used by this URLResource.
105: * @return a URL object.
106: */
107: public synchronized URL getURL() {
108: if (isReference()) {
109: return ((URLResource) getCheckedRef()).getURL();
110: }
111: return url;
112: }
113:
114: /**
115: * Overrides the super version.
116: * @param r the Reference to set.
117: */
118: public synchronized void setRefid(Reference r) {
119: //not using the accessor in this case to avoid side effects
120: if (url != null) {
121: throw tooManyAttributes();
122: }
123: super .setRefid(r);
124: }
125:
126: /**
127: * Get the name of this URLResource
128: * (its file component minus the leading separator).
129: * @return the name of this resource.
130: */
131: public synchronized String getName() {
132: return isReference() ? ((Resource) getCheckedRef()).getName()
133: : getURL().getFile().substring(1);
134: }
135:
136: /**
137: * Return this URLResource formatted as a String.
138: * @return a String representation of this URLResource.
139: */
140: public synchronized String toString() {
141: return isReference() ? getCheckedRef().toString() : String
142: .valueOf(getURL());
143: }
144:
145: /**
146: * Find out whether the URL exists .
147: * @return true if this resource exists.
148: */
149: public synchronized boolean isExists() {
150: if (isReference()) {
151: return ((Resource) getCheckedRef()).isExists();
152: }
153: return isExists(false);
154: }
155:
156: /**
157: * Find out whether the URL exists, and close the connection
158: * opened to the URL if closeConnection is true.
159: *
160: * Note that this method does ensure that if:
161: * - the resource exists (if it returns true)
162: * - and if the current object is not a reference
163: * (isReference() returns false)
164: * - and if it was called with closeConnection to false,
165: *
166: * then the connection to the URL (stored in the conn
167: * private field) will be opened, and require to be closed
168: * by the caller.
169: *
170: * @param closeConnection true if the connection should be closed
171: * after the call, false if it should stay open.
172: * @return true if this resource exists.
173: */
174: private synchronized boolean isExists(boolean closeConnection) {
175: if (getURL() == null) {
176: return false;
177: }
178: try {
179: connect();
180: return true;
181: } catch (IOException e) {
182: return false;
183: } finally {
184: if (closeConnection) {
185: close();
186: }
187: }
188: }
189:
190: /**
191: * Tells the modification time in milliseconds since 01.01.1970 .
192: *
193: * @return 0 if the resource does not exist to mirror the behavior
194: * of {@link java.io.File File}.
195: */
196: public synchronized long getLastModified() {
197: if (isReference()) {
198: return ((Resource) getCheckedRef()).getLastModified();
199: }
200: if (!isExists(false)) {
201: return 0L;
202: }
203: return conn.getLastModified();
204: }
205:
206: /**
207: * Tells if the resource is a directory.
208: * @return boolean whether the resource is a directory.
209: */
210: public synchronized boolean isDirectory() {
211: return isReference() ? ((Resource) getCheckedRef())
212: .isDirectory() : getName().endsWith("/");
213: }
214:
215: /**
216: * Get the size of this Resource.
217: * @return the size, as a long, 0 if the Resource does not exist (for
218: * compatibility with java.io.File), or UNKNOWN_SIZE if not known.
219: */
220: public synchronized long getSize() {
221: if (isReference()) {
222: return ((Resource) getCheckedRef()).getSize();
223: }
224: if (!isExists(false)) {
225: return 0L;
226: }
227: try {
228: connect();
229: long contentlength = conn.getContentLength();
230: close();
231: return contentlength;
232: } catch (IOException e) {
233: return UNKNOWN_SIZE;
234: }
235: }
236:
237: /**
238: * Test whether an Object equals this URLResource.
239: * @param another the other Object to compare.
240: * @return true if the specified Object is equal to this Resource.
241: */
242: public synchronized boolean equals(Object another) {
243: if (this == another) {
244: return true;
245: }
246: if (isReference()) {
247: return getCheckedRef().equals(another);
248: }
249: if (!(another.getClass().equals(getClass()))) {
250: return false;
251: }
252: URLResource otheru = (URLResource) another;
253: return getURL() == null ? otheru.getURL() == null : getURL()
254: .equals(otheru.getURL());
255: }
256:
257: /**
258: * Get the hash code for this Resource.
259: * @return hash code as int.
260: */
261: public synchronized int hashCode() {
262: if (isReference()) {
263: return getCheckedRef().hashCode();
264: }
265: return MAGIC
266: * ((getURL() == null) ? NULL_URL : getURL().hashCode());
267: }
268:
269: /**
270: * Get an InputStream for the Resource.
271: * @return an InputStream containing this Resource's content.
272: * @throws IOException if unable to provide the content of this
273: * Resource as a stream.
274: * @throws UnsupportedOperationException if InputStreams are not
275: * supported for this Resource type.
276: */
277: public synchronized InputStream getInputStream() throws IOException {
278: if (isReference()) {
279: return ((Resource) getCheckedRef()).getInputStream();
280: }
281: connect();
282: try {
283: return conn.getInputStream();
284: } finally {
285: conn = null;
286: }
287: }
288:
289: /**
290: * Get an OutputStream for the Resource.
291: * @return an OutputStream to which content can be written.
292: * @throws IOException if unable to provide the content of this
293: * Resource as a stream.
294: * @throws UnsupportedOperationException if OutputStreams are not
295: * supported for this Resource type.
296: * @throws IOException if the URL cannot be opened.
297: */
298: public synchronized OutputStream getOutputStream()
299: throws IOException {
300: if (isReference()) {
301: return ((Resource) getCheckedRef()).getOutputStream();
302: }
303: connect();
304: try {
305: return conn.getOutputStream();
306: } finally {
307: conn = null;
308: }
309: }
310:
311: /**
312: * Ensure that we have a connection.
313: * @throws IOException if the connection cannot be established.
314: */
315: protected synchronized void connect() throws IOException {
316: URL u = getURL();
317: if (u == null) {
318: throw new BuildException("URL not set");
319: }
320: if (conn == null) {
321: try {
322: conn = u.openConnection();
323: conn.connect();
324: } catch (IOException e) {
325: log(e.toString(), Project.MSG_ERR);
326: conn = null;
327: throw e;
328: }
329: }
330: }
331:
332: /**
333: * Closes the URL connection if:
334: * - it is opened (i.e. the field conn is not null)
335: * - this type of URLConnection supports some sort of close mechanism
336: *
337: * This method ensures the field conn will be null after the call.
338: *
339: */
340: private synchronized void close() {
341: if (conn != null) {
342: try {
343: if (conn instanceof JarURLConnection) {
344: JarURLConnection juc = (JarURLConnection) conn;
345: JarFile jf = juc.getJarFile();
346: jf.close();
347: jf = null;
348: } else if (conn instanceof HttpURLConnection) {
349: ((HttpURLConnection) conn).disconnect();
350: }
351: } catch (IOException exc) {
352: //ignore
353: } finally {
354: conn = null;
355: }
356: }
357: }
358:
359: private static URL newURL(String u) {
360: try {
361: return new URL(u);
362: } catch (MalformedURLException e) {
363: throw new BuildException(e);
364: }
365: }
366:
367: }
|