Package net.sf.fmj.media.protocol

Source Code of net.sf.fmj.media.protocol.URLDataSource$URLSourceStream

package net.sf.fmj.media.protocol;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.media.MediaLocator;
import javax.media.Time;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.PullDataSource;
import javax.media.protocol.PullSourceStream;
import javax.media.protocol.SourceCloneable;

import com.lti.utils.PathUtils;

import net.sf.fmj.media.MimeManager;
import net.sf.fmj.utility.LoggerSingleton;
import net.sf.fmj.utility.URLUtils;

/**
* URL DataSource.  Used by default when there is no more specific protocol handler available.
* @author Ken Larson
*
*/
public class URLDataSource extends PullDataSource implements SourceCloneable
{
  private static final Logger logger = LoggerSingleton.logger;
 
  protected URLConnection conn;

  protected boolean connected = false;

  private String contentTypeStr;
  private ContentDescriptor contentType;

  protected URLSourceStream[] sources;
 
  protected URLDataSource()
  {  super();
  }


  public URLDataSource(URL url)
  {  setLocator(new MediaLocator(url));
  }

  public javax.media.protocol.DataSource createClone()
  {
    final URLDataSource d;
    try
    {
      d = new URLDataSource(getLocator().getURL());
    } catch (MalformedURLException e)
    {
      logger.log(Level.WARNING, "" + e, e);
      return null// according to the API, return null on failure.
    }
   
    if (connected)
    { 
      try
      {
        d.connect();
      }
      catch (IOException e)
      { 
        logger.log(Level.WARNING, "" + e, e);
        return null// according to the API, return null on failure.
      }
    }
   
    return d; 
  }
 
  @Override
  public PullSourceStream[] getStreams()
  { 
    if (!connected)
      throw new Error("Unconnected source.");
    return sources;
  }
 
  /** Strips trailing ; and anything after it.  Is generally only used for multipart content. */
  private String stripTrailer(String contentType)
  {  final int index = contentType.indexOf(";");
    if (index < 0)
      return contentType;
    final String result = contentType.substring(0, index);
    return result;
   
  }

  @Override
  public void connect() throws IOException
 
    // we allow a re-connection even if we are connected, due to an oddity in the way Manager works. See comments there
    // in createPlayer(MediaLocator sourceLocator).
//    if (connected)  // TODO: FMJ tends to call this twice.  Check with JMF to see if that is normal.
//      return;
   
    URL url = getLocator().getURL();
   
    // TODO:
    // it seems that FMJ's use of relative file URLs may be nonstandard.
    // see http://en.wikipedia.org/wiki/File_URI_scheme and the things it links to.
   
    // workaround because URL.openConnection does not like relative file URLs.
    if (url.getProtocol().equals("file"))
    {  final String newUrlStr = URLUtils.createAbsoluteFileUrl(url.toExternalForm());
      if (newUrlStr != null)
      {
        if (!newUrlStr.toString().equals(url.toExternalForm()))
        {
          logger.warning("Changing file URL to absolute for URL.openConnection, from " + url.toExternalForm() + " to " + newUrlStr);
          url = new URL(newUrlStr);
        }
      }
    }
   
    conn = url.openConnection();
   
    // TODO: why does Java do this with relative file URLs, like file://samplemedia/safexmas.mov
    // it turns this into ftp://samplemedia/safexmas.mov
    // very strange.
    // typically sun.net.www.protocol.ftp.FtpURLConnection
    // this shouldn't happen anymore because of the relative file URL transformation above.
    if (!url.getProtocol().equals("ftp") && conn.getURL().getProtocol().equals("ftp"))
    {  logger.warning("URL.openConnection() morphed " + url + " to " + conn.getURL());
      throw new IOException("URL.openConnection() returned an FTP connection for a non-ftp url: " + url);
    }   
   
    if (conn instanceof HttpURLConnection)
    {  // TODO: this is probably why JMF has explicit HTTP and FTP data sources - so we can check things explicitly.
      final HttpURLConnection huc = (HttpURLConnection) conn;
      huc.connect();
     
      final int code = huc.getResponseCode();
      if (!(code >= 200 && code < 300))
      {  huc.disconnect();
        throw new IOException("HTTP response code: " + code);
     
     
      // some web servers will give the wrong content type.  It is hard to say whether we should just
      // always ignore the web server's content type, and just check the extension, or whether we should
      // only do it for clearly wrong types.  For now, we'll just do it for clearly wrong types.
     
      logger.finer("URL: " + url);
      logger.finer("Response code: " + code);
      logger.finer("Full content type: " + conn.getContentType());
     
      boolean contentTypeSet = false;
      if (stripTrailer(conn.getContentType()).equals("text/plain"))
      {
        // see if the URL has an extension, and guess based on that.
        final String ext = PathUtils.extractExtension(url.getPath())// this is a bit of a hack, PathUtils.extractExtension was designed for file paths, but it works here.
        if (ext != null)
        {
          // TODO: what if ext is null?
          final String result = MimeManager.getMimeType(ext);
          if (result != null)
          {  contentTypeStr = ContentDescriptor.mimeTypeToPackageName(result);
            contentTypeSet = true;
           
            logger.fine("Received content type " + conn.getContentType() + "; overriding based on extension, to: " + result);
          }
        }

      }
     
     
        // TODO: what is the right place to apply ContentDescriptor.mimeTypeToPackageName?
      if (!contentTypeSet)
        contentTypeStr = ContentDescriptor.mimeTypeToPackageName(stripTrailer(conn.getContentType()));
     
    }
    else
    {
      conn.connect();
      // TODO: what is the right place to apply ContentDescriptor.mimeTypeToPackageName?
      contentTypeStr = ContentDescriptor.mimeTypeToPackageName(conn.getContentType());
    }
   

    contentType = new ContentDescriptor(contentTypeStr);
    sources = new URLSourceStream[1];
    sources[0] = new URLSourceStream();
                                 
    connected = true;
  }

  @Override
  public String getContentType()
  {  return contentTypeStr;
  }

  @Override
  public void disconnect()
  {  if (!connected)
      return;
 
    if (conn != null)
    {
      if (conn instanceof HttpURLConnection)
      {
        final HttpURLConnection huc = (HttpURLConnection) conn;
        huc.disconnect();
      }
      // TODO: others
    }
   
    connected = false;
  }

  @Override
  public void start() throws java.io.IOException
 
    //throw new UnsupportedOperationException(); // TODO - what to do?
  }

  @Override
  public void stop() throws java.io.IOException
  //throw new UnsupportedOperationException(); // TODO - what to do?
  }

  @Override
  public Time getDuration()
  {  return Time.TIME_UNKNOWN;  // TODO: any case where we know the duration?
  }

  @Override
  public Object[] getControls()
  {  return new Object[0];
  }

  @Override
  public Object getControl(String controlName)
  {  return null;
  }

  class URLSourceStream implements PullSourceStream
  {

    private boolean endOfStream = false;
    public boolean endOfStream()
    {
      return endOfStream;
    }

    public ContentDescriptor getContentDescriptor()
    {
      return contentType;
    }

    public long getContentLength()
    {
     
      return conn.getContentLength();    // returns -1 if unknown, which is the same as LENGTH_UNKNOWN
    }

    public int read(byte[] buffer, int offset, int length) throws IOException
    {
      final int result = conn.getInputStream().read(buffer, offset, length)// TODO: does this handle the requirement of not returning 0 unless passed in 0?
      if (result == -1) // end of stream
        endOfStream = true;
     
      return result;
    }

    public boolean willReadBlock()
    {
      try
      {
        return conn.getInputStream().available() <= 0;
      } catch (IOException e)
      {
        return true;
      }
    }

    public Object getControl(String controlType)
    {
      return null;
    }

    public Object[] getControls()
    {
      return new Object[0];
    }
  }
}
TOP

Related Classes of net.sf.fmj.media.protocol.URLDataSource$URLSourceStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.