Package org.xlightweb

Source Code of org.xlightweb.FileDataSource$FileSender

/*
*  Copyright (c) xlightweb.org, 2008 - 2010. All rights reserved.
*
*  This library is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public
*  License as published by the Free Software Foundation; either
*  version 2.1 of the License, or (at your option) any later version.
*
*  This library is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*  Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
* The latest copy of this software may be found on http://www.xlightweb.org/
*/
package org.xlightweb;

import java.io.File;


import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;



import org.xlightweb.AbstractHttpConnection.IMultimodeExecutor;
import org.xsocket.DataConverter;
import org.xsocket.Execution;
import org.xsocket.connection.IWriteCompletionHandler;
import org.xsocket.connection.IConnection.FlushMode;




/**
* file  body data source
*
* @author grro@xlightweb.org
*
*/
final class FileDataSource extends NonBlockingBodyDataSource implements IForwardable {

    private final File file;
    private final long length;
    private final String range;
   
    FileDataSource(IHeader header, IMultimodeExecutor executor, File file) throws IOException {
      this(header, executor, file, null);
    }
   
    FileDataSource(IHeader header, IMultimodeExecutor executor, File file, String range) throws IOException {
        super(header, executor);
       
        this.file = file;
        this.range = range;
        length = file.length();
    }
   
    long getLength() {
        return length;
    }

    public void forwardTo(final BodyDataSink bodyDataSink) throws IOException {
      forwardTo(bodyDataSink, null);
    }
   
    public void forwardTo(BodyDataSink bodyDataSink, IBodyCompleteListener completeListener) throws IOException {
        FileSender sendFile = new FileSender(bodyDataSink.getId(), bodyDataSink, file, range, completeListener);
        sendFile.run();
    }
  
   


   
    File getFile() {
        return file;
    }
  
    @Override
    boolean isForwardable() {
        return true;
    }
   
    /**
     * {@inheritDoc}
     */
    @Override
    protected boolean isNetworkendpoint() {
        return false;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    String getId() {
        return Integer.toString(hashCode());
    }

   
    /**
     * {@inheritDoc}
     */
    @Override
    boolean suspend() throws IOException {
        return false;
    }


   
    /**
     * {@inheritDoc}
     */
    @Override
    boolean resume() throws IOException {
        return false;
    }
 

    /**
     * {@inheritDoc}
     */
    @Override
    void onClose() {
    }

   
    /**
     * {@inheritDoc}
     */
    @Override
    void onDestroy(String reason) {
    }
   
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
       
        InputStream is = null;
        try {
            is = new FileInputStream(file);
           
            byte[] data = new byte[(int) getLength()];
            int read = is.read(data);
            sb.append(DataConverter.toString(DataConverter.toByteBuffer(data, 0, read), getEncoding()));
           
        } catch (IOException ignore) {
         
        } finally {
          if (is != null) {
            try {
              is.close();
            } catch (IOException ignore) { }
          }
        }
       
        return sb.toString();
    }
   
   
    @Execution(Execution.MULTITHREADED// necessary to avoid deep stacks
    static class FileSender implements Runnable, IWriteCompletionHandler {
       
        private static final Logger LOG = Logger.getLogger(FileSender.class.getName());

        static final int TRANSFER_BYTE_BUFFER_MAX_MAP_SIZE = Integer.parseInt(System.getProperty("org.xsocket.connection.transfer.mappedbytebuffer.maxsize", "65536"));
       
        private final String id;
        private final File file;
        private final RandomAccessFile raf;
        private final FileChannel fc;
        private final BodyDataSink dataSink;
        private final IBodyCompleteListener completeListener;
       
        private boolean isOpen = true;
       
        private long length = 0;
       
        private final AtomicLong remaining = new AtomicLong(0);
        private final AtomicLong offset = new AtomicLong(0);
       
        public FileSender(String id, BodyDataSink dataSink, File file, String range, IBodyCompleteListener completeListener) throws IOException {
           
            if (!file.exists()) {
                throw new IOException("file " + file.getAbsolutePath() + " does not exist");
            }
           
            this.id = id;
            this.file = file;
            this.completeListener = completeListener;
            this.raf = new RandomAccessFile(file, "r");
               
            fc = raf.getChannel();
            this.dataSink = dataSink;          
            dataSink.setFlushmode(FlushMode.ASYNC);
           
            if (range != null) {
              int[] positions = HttpUtils.computeFromRangePosition(range, (int) file.length());
             
              offset.set(positions[0]);
              remaining.set((positions[1] - positions[0]) + 1);
            } else {
              offset.set(0);
              remaining.set(file.length());
            }
        }
       
        final String getAbsolutePath() {
            return file.getAbsolutePath();
        }
       
        public final void run() {
            try {
                write();
            } catch (IOException ioe) {
                throw new RuntimeException(ioe);
            }
        }
           
        public final void onWritten(int written) throws IOException {
           
            if (LOG.isLoggable(Level.FINE)) {
                if (remaining.get() > 0) {
                    LOG.fine("[" + id + "] data (size=" + written + " bytes) has been written. Writing next chunk");
                } else {
                    LOG.fine("[" + id + "] data (size=" + written + " bytes) has been written.");
                }
            }
           
            write();
        }
       
       
        private void write() throws IOException {
           
            // remaining data to write?
            if (remaining.get() > 0) {
               
                // limit the buffer allocation size
                if (remaining.get() > TRANSFER_BYTE_BUFFER_MAX_MAP_SIZE) {
                    length = TRANSFER_BYTE_BUFFER_MAX_MAP_SIZE;
                } else {
                    length = remaining.get();
                }
               
                MappedByteBuffer buffer = fc.map(MapMode.READ_ONLY, offset.get(), length);
                ByteBuffer[] bufs = new ByteBuffer[] { buffer };

                if (LOG.isLoggable(Level.FINE)) {
                    LOG.fine("[" + id + "] writing data (size=" + length + " bytes)");
                }              

                // set the pointers before writing -> write callback occurs within call
                offset.addAndGet(length);
                remaining.set(remaining.get() - length);
               
                doWrite(bufs);
               

            // no, closing dataSink
            } else {
                if (isOpen) {
                    isOpen = false;
                    closeFile();
                    if (completeListener == null) {
                  dataSink.close();
                    } else {
                      completeListener.onComplete();
                    }
                }
            }
        }
   
        void doWrite(ByteBuffer[] bufs) throws IOException {
          dataSink.write(bufs, this);
        }

       
        public final void onException(IOException ioe) {
            if (isOpen) {
                isOpen = false;
                closeFile();
            }
            dataSink.destroy();
        }
       
       
        void closeFile() {
            try {
                fc.close();
                raf.close();
            } catch (IOException ioe) {
                if (LOG.isLoggable(Level.FINE)) {
                    LOG.fine("error occured by closing file channel " + getAbsolutePath() + " " + ioe.toString());
                }
            }
        }   
    }
}
TOP

Related Classes of org.xlightweb.FileDataSource$FileSender

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.