/* * Copyright (C) 2014 Shashank Tulsyan * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package neembuu.release1.defaultImpl.file.split; import java.io.FileOutputStream; import java.nio.channels.FileChannel; import java.nio.channels.SeekableByteChannel; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import neembuu.release1.api.file.NeembuuFile; import jpfm.FileAttributesProvider; import jpfm.mount.BasicCascadeMount; import neembuu.diskmanager.Session; import neembuu.rangearray.RangeArrayFactory; import neembuu.rangearray.UIRangeArrayAccess; import neembuu.release1.api.file.PropertyProvider; import neembuu.release1.defaultImpl.file.BasicPropertyProvider; import neembuu.release1.defaultImpl.file.NeembuuFileWrapSCF; import neembuu.vfs.file.AutoCompleteControls; import neembuu.vfs.file.FileBeingDownloaded; import neembuu.vfs.file.MinimumFileInfo; import neembuu.vfs.file.RequestPatternListener; import neembuu.vfs.file.SeekableConnectionFile; import neembuu.vfs.readmanager.ReadRequestState; import neembuu.vfs.readmanager.TotalFileReadStatistics; /** * * @author Shashank Tulsyan */ public class SplitGroupSession implements NeembuuFile { private final BasicCascadeMount bcm; private final List<SeekableConnectionFile> connectionFiles; private final Session s; private final FileAttributesProvider fap; private final UIRangeArrayAccess<ReadRequestState> totalRegion; private final TotalFileReadStatistics totalFileReadStatistics; private final AtomicBoolean completelyClosed = new AtomicBoolean(false); public static final class Holder { RequestPatternListener requestPatternListener; } private final Holder h = new Holder(); private final BasicPropertyProvider bpp = new BasicPropertyProvider(); public SplitGroupSession(BasicCascadeMount bcm, List<SeekableConnectionFile> connectionFiles,Session s) { this.bcm = bcm; this.s = s; this.connectionFiles = connectionFiles; fap = bcm.getFileSytem().list(bcm.getFileSytem().getRootAttributes().getFileDescriptor().getFileId()) .iterator().next(); List<UIRangeArrayAccess<ReadRequestState>> accesses = new LinkedList<UIRangeArrayAccess<ReadRequestState>>(); for (SeekableConnectionFile seekableConnectionFile : connectionFiles) { accesses.add(seekableConnectionFile.getRegionHandlers()); } ReadRequestRescaler rrr = new ReadRequestRescaler(); totalRegion = RangeArrayFactory.merge(accesses,rrr); totalFileReadStatistics = new TotalFileReadStatistics_forSplits(connectionFiles); AggregateRequestPatternListener.initializeAggregateRequestListener(connectionFiles,h); } @Override public List<NeembuuFile> getVariants() { final ArrayList<NeembuuFile> neembuuFiles = new ArrayList<>(); for (SeekableConnectionFile seekableConnectionFile : connectionFiles) { neembuuFiles.add(new NeembuuFileWrapSCF(seekableConnectionFile, null, null/*s*/, null,null)); } return neembuuFiles; } @Override public Session getSession() { return s; } @Override public PropertyProvider getPropertyProvider() { return bpp; } @Override public String[] relativePathInVirtualFileSystem(){ return new String[]{bcm.getFileSytem().getRootAttributes().getName(),fap.getName()}; } @Override public void closeCompletely()throws Exception{ List<Exception> es = new LinkedList<Exception>(); if(completelyClosed.compareAndSet(false, true)){ for (SeekableConnectionFile file : connectionFiles) { try{ file.closeCompletely();} catch(Exception a){ es.add(a); } }}else { throw new IllegalStateException("Already completely closed"); }if(!es.isEmpty()){ throw new AggregateException("Could not closeCompletely some files", null, es.toArray(new Exception[es.size()])); }} @Override public void saveACopy(java.io.File outputFilePath)throws Exception{ // We shall save only the merge. Saving splits is not useful for the user try(FileChannel dest = new FileOutputStream(outputFilePath).getChannel()){ long[]cummulativeFileSizes = new long[connectionFiles.size()]; int i = 0; SeekableByteChannel osbc; Collections.sort(connectionFiles, new Comparator<SeekableConnectionFile>() { @Override public int compare(SeekableConnectionFile o1, SeekableConnectionFile o2) { return o1.getDownloadConstrainHandler().index() - o2.getDownloadConstrainHandler().index(); } }); for(SeekableConnectionFile file : connectionFiles){ if(i==0){ osbc = dest; cummulativeFileSizes[i]=0+file.getFileSize(); }else{ dest.position(cummulativeFileSizes[i]); osbc = new OffsettedSeekableByteChannel(cummulativeFileSizes[i-1], dest); cummulativeFileSizes[i]=cummulativeFileSizes[i-1]+file.getFileSize(); }i++; file.getFileStorageManager().copyIfCompleteTo(osbc, file.getFileSize()); } dest.force(true); } } private final FileBeingDownloaded fileBeingDownloaded = new FileBeingDownloaded(){ @Override public void addRequestPatternListener(RequestPatternListener rpl) { if(h.requestPatternListener==null){h.requestPatternListener = rpl; }else { throw new UnsupportedOperationException("Only one supported for now "+h.requestPatternListener);}} @Override public void removeRequestPatternListener(RequestPatternListener rpl) { if(h.requestPatternListener==rpl){ h.requestPatternListener = null;}} @Override public UIRangeArrayAccess getRegionHandlers() {return totalRegion;} @Override public TotalFileReadStatistics getTotalFileReadStatistics() {return totalFileReadStatistics; } @Override public boolean isAutoCompleteEnabled() { return autoCompleteControls.isAutoCompleteEnabled(); } @Override public String getName() { return minimumFileInfo.getName(); } @Override public long getFileSize() { return minimumFileInfo.getFileSize(); }}; private final MinimumFileInfo minimumFileInfo = new MinimumFileInfo() { @Override public String getName() { return fap.getName(); } @Override public long getFileSize() { return fap.getFileSize(); }}; private final AutoCompleteControls autoCompleteControls = new AutoCompleteControls() { private boolean autoCompleteEnabled = true; @Override public boolean isAutoCompleteEnabled() { return autoCompleteEnabled; } @Override public void setAutoCompleteEnabled(boolean autoCompleteEnabled) { if(this.autoCompleteEnabled != autoCompleteEnabled){ for (SeekableConnectionFile seekableConnectionFile : connectionFiles) { seekableConnectionFile.setAutoCompleteEnabled(autoCompleteEnabled); } } this.autoCompleteEnabled = autoCompleteEnabled; }}; @Override public boolean mayBeSaved() { boolean mayBeSaved = true; for(SeekableConnectionFile file : connectionFiles){ if(!file.getDownloadConstrainHandler().isComplete()) { mayBeSaved = false;break; } } return mayBeSaved; } @Override public MinimumFileInfo getMinimumFileInfo(){ return minimumFileInfo; } @Override public FileBeingDownloaded fileBeingDownloaded(){ return fileBeingDownloaded; } @Override public AutoCompleteControls autoCompleteControls(){ return autoCompleteControls; } @Override public boolean isCompletelyClosed() { return completelyClosed.get();} @Override public void removeFromFileSystem() throws Exception{ bcm.unMount(); } @Override public void addToFileSystem(){ /* When cascade mount is create, then it automatically added as well.*/} }