001/*
002 * JPPF.
003 * Copyright (C) 2005-2015 JPPF Team.
004 * http://www.jppf.org
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.jppf.location;
020
021import java.io.*;
022import java.net.*;
023
024import org.jppf.utils.*;
025import org.slf4j.*;
026
027/**
028 * Wrapper for manipulating data from a URL.
029 * This implementation of the {@link Location} interface allows writing to and reading from a URL.
030 * @author Laurent Cohen
031 */
032public class URLLocation extends AbstractLocation<URL>
033{
034  /**
035   * Explicit serialVersionUID.
036   */
037  private static final long serialVersionUID = 1L;
038  /**
039   * Logger for this class.
040   */
041  private static Logger log = LoggerFactory.getLogger(URLLocation.class);
042  /**
043   * Determines whether the debug level is enabled in the logging configuration, without the cost of a method call.
044   */
045  private static boolean debugEnabled = LoggingUtils.isDebugEnabled(log);
046  /**
047   * The size of the artifact pointed to by this URL location.
048   * We attempt to cache it to avoid looking it up by opening a connection every time.
049   */
050  private long size =-1L;
051  /**
052   * Determines whether an attempt to obtain the size of the content has already been made.
053   * This attribute is transient so that it is "reset" whenever it is deserialized in a remote JVM.
054   * This forces a new attempt to be made to get the size, in case the URL was reachable from where this location was created.
055   */
056  private transient boolean sizeAttemptMade = false;
057
058  /**
059   * Initialize this location with the specified file path.
060   * @param url a {@link URL}.
061   */
062  public URLLocation(final URL url)
063  {
064    super(url);
065  }
066
067  /**
068   * Initialize this location with the specified file path.
069   * @param url a URL in string format.
070   * @throws MalformedURLException if the url is malformed.
071   */
072  public URLLocation(final String url) throws MalformedURLException
073  {
074    super(new URL(url));
075  }
076
077  @Override
078  public InputStream getInputStream() throws Exception
079  {
080    if ("file".equalsIgnoreCase(path.getProtocol())) return new BufferedInputStream(new FileInputStream(path.getPath()));
081    return path.openStream();
082  }
083
084  @Override
085  public OutputStream getOutputStream() throws Exception
086  {
087    // URLConnection.getOutputStream() throws an UnknownServiceException for file urls.
088    // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4485313
089    if ("file".equalsIgnoreCase(path.getProtocol())) return new BufferedOutputStream(new FileOutputStream(path.getPath()));
090    URLConnection conn = path.openConnection();
091    conn.setDoOutput(true);
092    return conn.getOutputStream();
093  }
094
095  /**
096   * This method attemps to get the size of the content pointed to by the URL. It may not always be possible,
097   * depending on the protocol and what's at the other end.
098   * @return the content size if it is available, or -1 if it isn't.
099   */
100  @Override
101  public long size()
102  {
103    if ((size < 0L) && !sizeAttemptMade)
104    {
105      try
106      {
107        // for file URLs, opening a connection causes an input stream
108        // to be created, which is never released
109        if ("file".equalsIgnoreCase(path.getProtocol()))
110        {
111          File file = new File(path.getPath());
112          size = file.length();
113        }
114        else
115        {
116          URLConnection c = path.openConnection();
117          c.connect();
118          size = c.getContentLengthLong();
119        }
120      }
121      catch (Exception e)
122      {
123        String msg = "Error while trying to get the content length of {} : {}";
124        if (debugEnabled) log.debug(msg, this, ExceptionUtils.getStackTrace(e));
125        else log.warn(msg, this, ExceptionUtils.getMessage(e));
126      }
127      finally
128      {
129        sizeAttemptMade = true;
130      }
131    }
132    return size;
133  }
134}