OpenNapDownloadContainer.java
| Index Score | ||
|---|---|---|
![]() |
![]() |
org.xnap.plugin.opennap.net |
![]() |
![]() |
XNap 3 |
View: Reasons, Metrics, Source Code
These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.
/*
* XNap - A P2P framework and client.
*
* See the file AUTHORS for copyright information.
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.xnap.plugin.opennap.net;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Iterator;
import javax.swing.Action;
import javax.swing.Icon;
import org.xnap.gui.XNapFrame;
import org.xnap.XNap;
import org.xnap.action.*;
import org.xnap.gui.action.*;
import org.xnap.peer.Peer;
import org.xnap.plugin.Plugin;
import org.xnap.plugin.opennap.OpenNapPlugin;
import org.xnap.plugin.opennap.gui.OpenNapDownloadContainerEditorDialog;
import org.xnap.search.DefaultSearchFilter;
import org.xnap.search.Search;
import org.xnap.search.SearchFilter;
import org.xnap.search.SearchHandler;
import org.xnap.search.SearchResult;
import org.xnap.transfer.AbstractDownload;
import org.xnap.transfer.DownloadManager;
import org.xnap.transfer.Queueable;
import org.xnap.transfer.Segment;
import org.xnap.transfer.action.AbstractDeleteAction;
import org.xnap.transfer.action.AbstractEditAction;
import org.xnap.transfer.action.AbstractFindMoreSourcesAction;
import org.xnap.transfer.action.AbstractStartAction;
import org.xnap.transfer.action.AbstractStopAction;
import org.xnap.transfer.action.AbstractTransferAction;
import org.xnap.util.FileHelper;
import org.xnap.util.FiniteStateMachine;
import org.xnap.util.IllegalOperationException;
import org.xnap.util.Preferences;
import org.xnap.util.*;
import org.xnap.util.State;
/**
*
*/
public class OpenNapDownloadContainer extends AbstractDownload
implements Queueable, SearchHandler {
//--- Constant(s) ---
/**
* The initial auto search interval. The interval is doubled with
* each search. */
public static final int INITIAL_SEARCH_INTERVAL = 30 * 60 * 1000; // 30 min
/**
* The upper bound for the search interval. */
public static final int MAX_SEARCH_INTERVAL = 3 * 60 * 60 * 1000; // 3 h
/**
* Maximum number of auto searches. */
public static final int MAX_SEARCH_COUNT = 10;
/**
* The state transition table. */
private static final Hashtable TRANSITION_TABLE;
static {
State[][] table = new State[][] {
{ State.NOT_STARTED,
State.RUNNING, State.DELETED, },
{ State.RUNNING,
State.STOPPING, State.SUCCEEDED, },
{ State.STOPPING,
State.NOT_STARTED, },
{ State.SUCCEEDED,
State.DELETED, },
};
TRANSITION_TABLE = FiniteStateMachine.createStateTable(table);
}
//--- Data Field(s) ---
private OpenNapDownloadContainerData data;
private OpenNapSegmentManager segmentManager;
private long enqueueTime;
private int queuePosition;
private StateMachine sm = new StateMachine();
private long bytesTransferred;
private long totalBytesTransferred;
private DefaultSearchFilter filter;
private int runningCount = 0;
private int minQueuePos = -1;
private ToggleAction autoSearchAction = new AutoSearchAction();
private AbstractTransferAction deleteAction = new DeleteAction();
private AbstractTransferAction startAction = new StartAction();
private AbstractTransferAction stopAction = new StopAction();
/**
* The number of children that are in downloading state.
*/
private int downloadingCount = 0;
//--- Constructor(s) ---
public OpenNapDownloadContainer(OpenNapSearchResult results[])
{
data = new OpenNapDownloadContainerData();
data.filename = results[0].getShortFilename();
data.filesize = results[0].getFilesize();
data.autoSearchingEnabled
= Preferences.getInstance().getAlwaysAutoDownload();
if (results[0].getFilter() != null) {
setSearchFilter(results[0].getFilter());
}
else {
DefaultSearchFilter filter = new DefaultSearchFilter();
filter.put
(SearchFilter.TEXT,
StringHelper.stripExtra(FileHelper.name(data.filename)));
setSearchFilter(filter);
}
segmentManager = new OpenNapSegmentManager
(this, OpenNapPlugin.getPreferences().getMultiSourceDownloading());
for (int i = 0; i < results.length; i++) {
add(results[i], false);
}
initialize();
File path = new File(Preferences.getInstance().getIncompleteDir());
OpenNapPlugin.getTransferManager().getResumeRepository().add
(path, this.data);
enqueueTime = System.currentTimeMillis();
DownloadManager.getInstance().getQueue().add(this);
}
/**
* Invoked by {@link OpenNapResumeRepository} when restoring download from
* resume data.
*/
public OpenNapDownloadContainer(OpenNapDownloadContainerData data)
{
this.data = data;
this.filter = createFilterFromData();
segmentManager = new OpenNapSegmentManager
(this, OpenNapPlugin.getPreferences().getMultiSourceDownloading(),
data.segments);
totalBytesTransferred = segmentManager.getBytesTransferred();
initialize();
}
//--- Methods ---
private void initialize()
{
deleteAction.setEnabledLater(true);
startAction.setEnabledLater(true);
stopAction.setEnabledLater(false);
autoSearchAction.setSelected(data.autoSearchingEnabled);
}
/**
* Creates a new download for result and adds it as a child. If a
* download for result is already existing, starts it if not
* running.
*
* @return true, if download for result was added or already
* existing; false, otherwise */
public boolean add(OpenNapSearchResult result, boolean matchFilter)
{
synchronized (sm) {
// check if there is already a child download that downloads
// the result
for (Iterator i = OpenNapDownloadContainer.this.iterator();
i.hasNext();) {
OpenNapDownload d = (OpenNapDownload)i.next();
if (result.equals(d.getResult())) {
if (d.isRestartable()) {
if (d.getResult().getOpenNapUser().isDownloadDenied()) {
d.setStateDescription(XNap.tr("Downloads from this peer are disabled"));
}
else {
d.start();
}
return true;
}
}
}
if (matchFilter) {
if (filter == null || !filter.matches(result)) {
return false;
}
}
OpenNapDownload d = new OpenNapDownload(this, result);
add(d);
if (d.getResult().getOpenNapUser().isDownloadDenied()) {
d.setStateDescription(XNap.tr("Downloads from this peer are disabled"));
}
else if (sm.getState() == State.RUNNING) {
d.start();
}
return true;
}
}
/**
* Notifies the {@link OpenNapTransferManager} that the download
* was removed. Invoked by {@link DownloadManager} when the
* download is done and clear finished is executed by user. */
public void cleared()
{
OpenNapPlugin.getTransferManager().remove(this);
}
public long getEnqueueTime()
{
return enqueueTime;
}
/**
*
*/
public String getFilename()
{
return data.filename;
}
/**
*
*/
public long getFilesize()
{
return data.filesize;
}
public Plugin getPlugin()
{
return OpenNapPlugin.getInstance();
}
/**
* @see xnap.transfer.Transfer#getActions()
*/
public Action[] getActions()
{
return new Action[] {
startAction,
stopAction,
new FindMoreSourcesAction(),
new EditAction(),
deleteAction,
null,
new OptionSubmenuAction(new Action[] { autoSearchAction }),
};
}
/**
*
*/
public long getBytesTransferred()
{
return bytesTransferred;
}
/**
* @see xnap.transfer.Transfer#getFile()
*/
public File getFile()
{
OpenNapSegment segment = segmentManager.getRoot();
return (segment != null) ? segment.getFile() : null;
}
public Icon getIcon()
{
return OpenNapPlugin.ICON_16;
}
/**
* Returns 1.
*/
public int getPriority()
{
return 1;
}
/**
* Returns the position in the {@link DownloadManager} queue.
*/
public int getQueuePosition()
{
return queuePosition;
}
/**
* @see xnap.transfer.Transfer#getPeer()
*/
public Peer getPeer()
{
return null;
}
/**
* @return a copy of the filter used for searching; null, if no
* search filter has been set, yet (i.e. download was started from
* browse) */
public SearchFilter getSearchFilter()
{
return (filter != null) ? (SearchFilter)filter.clone() : null;
}
public Segment[] getSegments()
{
return segmentManager.getSegments();
}
/**
* @see xnap.transfer.Transfer#getStatus()
*/
public String getStatus()
{
return sm.getDescription();
}
/**
* @see xnap.transfer.Transfer#getTotalBytesTransferred()
*/
public long getTotalBytesTransferred()
{
return totalBytesTransferred;
}
public boolean isAutoSearchingEnabled()
{
return data.autoSearchingEnabled;
}
public boolean isDone()
{
State s = sm.getState();
return s == State.SUCCEEDED;
}
/**
* Returns true, if the root segment is complete.
*/
private boolean isDownloadComplete()
{
OpenNapSegment segment = segmentManager.getRoot();
return segment != null && segment.isFinished()
&& segment.getEnd() == getFilesize();
}
public boolean isRunning()
{
return sm.getState() == State.RUNNING;
}
public void resultReceived(SearchResult result)
{
add((OpenNapSearchResult)result, true);
}
public void setAutoSearchingEnabled(boolean autoSearchingEnabled)
{
data.autoSearchingEnabled = autoSearchingEnabled;
}
public void setFilename(String filename)
{
data.filename = filename;
}
/**
* @param filter the search filter
*/
public void setSearchFilter(SearchFilter filter)
{
data.searchText = filter.getText();
data.realm = OpenNapPlugin.getSearchManager().getRealm
(filter.getMediaType());
this.filter = createFilterFromData();
}
void setSegments(OpenNapSegment[] segments)
{
data.segments = new OpenNapSegmentData[segments.length];
for (int i = 0; i < segments.length; i++) {
data.segments[i] = segments[i].getData();
}
}
private DefaultSearchFilter createFilterFromData()
{
DefaultSearchFilter filter = new DefaultSearchFilter();
filter.put(SearchFilter.TEXT, data.searchText);
if (data.realm != null) {
filter.put(SearchFilter.MEDIA_TYPE,
OpenNapPlugin.getSearchManager().getMediaType(data.realm));
}
filter.put(SearchFilter.MAX_FILESIZE, new Long(data.filesize));
filter.put(SearchFilter.MIN_FILESIZE, new Long(data.filesize));
return filter;
}
/**
* Invoked when the state of <code>search</code> changes.
*/
public void stateChanged(Search search)
{
if (search.isDone()) {
setStateDescription(XNap.tr("Finished search for more sources"));
}
}
/**
*
*/
public void setQueuePosition(int position)
{
queuePosition = position;
}
synchronized void commit(int transferred)
{
bytesTransferred += transferred;
totalBytesTransferred += transferred;
}
/**
* Invoked by {@link OpenNapDownloadRunner} objects.
*/
synchronized void done(OpenNapDownload d)
{
synchronized (sm) {
if (sm.getState() == State.SUCCEEDED) {
// another thread has already done the moving
return;
}
else if (sm.getState() == State.NOT_STARTED) {
logger.error("Illegal download state detected");
}
}
if (isDownloadComplete()) {
setStateDescription
(XNap.tr("Moving file to destination directory"));
try {
OpenNapSegment segment = getSegmentManager().getRoot();
String dir = FileHelper.getDownloadDir(getFilename());
File newFile = FileHelper.moveUnique
(segment.getFile(), dir, getFilename());
segment.setFile(newFile);
setState(State.SUCCEEDED);
}
catch (IOException e) {
logger.debug("could not rename finished file", e);
setState
(State.SUCCEEDED,
XNap.tr("Could not create file (check download dir)"));
}
}
else if (!d.isRestartable()) {
add(d.getResult(), false);
}
}
OpenNapSegmentManager getSegmentManager()
{
return segmentManager;
}
/**
* Invoked by {@link OpenNapDownload} children when child is queued. */
synchronized void remotelyQueued(int position)
{
if ((minQueuePos == -1 || position < minQueuePos) && position != 0) {
minQueuePos = -1;
for (Iterator i = OpenNapDownloadContainer.this.iterator();
i.hasNext();) {
OpenNapDownload d = (OpenNapDownload)i.next();
if (d.isQueued()) {
int pos = d.getQueuePosition();
if (pos < minQueuePos || minQueuePos == -1) {
minQueuePos = pos;
}
}
}
}
updateDescription();
}
private void searchForSources()
{
if (filter != null && filter.getText().length() > 0) {
Search s = OpenNapPlugin.getSearchManager().search(filter);
setStateDescription(XNap.tr("Searching for more sources") + "...");
s.start(this);
}
else {
setStateDescription(XNap.tr("Invalid search settings"));
}
}
private void setState(State newState, String description)
{
sm.setState(newState, description);
stateChanged();
}
private void setState(State newState)
{
sm.setState(newState);
stateChanged();
}
void setStateDescription(String description)
{
sm.setDescription(description);
stateChanged();
}
/**
* Invoked when the user manually starts the download or if it
* reaches to the top of the queue. */
public boolean startTransfer()
{
try {
setState(State.RUNNING);
synchronized (sm) {
for (Iterator i = OpenNapDownloadContainer.this.iterator();
i.hasNext();) {
((OpenNapDownload)i.next()).start();
}
}
}
catch (IllegalOperationException e) {
logger.error("unexpected exception", e);
}
return true;
}
/**
* Invoked by {@link OpenNapDownload} children to notify the
* container that a download has started. For each call to start()
* a call to {@link #stopped(OpenNapDownload)} needs to be made.
*
* @return true, if the start was accepted */
boolean started(OpenNapDownload d)
{
boolean notify = false;
synchronized (sm) {
if (sm.getState() == State.STOPPING
|| sm.getState() == State.SUCCEEDED) {
return false;
}
if (sm.getState() == State.NOT_STARTED) {
sm.setState(State.RUNNING);
notify = true;
}
runningCount++;
}
if (notify) {
stateChanged();
}
return true;
}
public void stop()
{
try {
setState(State.STOPPING);
minQueuePos = -1;
setQueuePosition(-1);
}
catch (IllegalOperationException e) {
// ignore exception, usually when this occurs we are
// usually already stopped and stop() is only invoked as a
// safe-guard
//logger.warn("unexpected state", e);
}
}
private void stopAllChildren()
{
synchronized (sm) {
for (Iterator i = OpenNapDownloadContainer.this.iterator();
i.hasNext();) {
((OpenNapDownload)i.next()).stop(XNap.tr("Stopped"));
}
}
}
/**
* Invoked by {@link OpenNapDownload} children.
*
* @see #started(OpenNapDownload)
*/
void stopped(OpenNapDownload d)
{
boolean notify = false;
synchronized (sm) {
runningCount--;
if (runningCount == 0 && sm.getState() == State.STOPPING) {
sm.setState(State.NOT_STARTED);
notify = true;
minQueuePos = -1;
updateDescription();
}
}
if (notify) {
stateChanged();
}
}
/**
* Invoked by {@link OpenNapDownload} children when DOWNLOADING
* state is entered. */
protected synchronized void transferStarted()
{
downloadingCount++;
if (downloadingCount == 1) {
bytesTransferred = 0;
super.transferStarted();
updateDescription();
}
}
/**
* Invoked by {@link OpenNapDownload} children when DOWNLOADING
* state is left. */
protected synchronized void transferStopped()
{
downloadingCount--;
if (downloadingCount == 0) {
super.transferStopped();
updateDescription();
}
}
private synchronized void updateDescription()
{
synchronized(sm) {
if (sm.getState() == State.RUNNING) {
if (downloadingCount > 0) {
sm.setDescription(XNap.tr("Downloading"));
setQueuePosition(0);
}
else if (minQueuePos > 0) {
sm.setDescription
(XNap.tr("Remotely queued ({0})",
new Integer(minQueuePos)));
setQueuePosition(minQueuePos);
}
else {
sm.setDescription(XNap.tr("Running"));
}
}
}
stateChanged();
}
//--- Inner Class(es) ---
private class StateMachine extends FiniteStateMachine
{
private AutoSearchTask searchTask;
//--- Constructor(s) ---
public StateMachine()
{
super(State.NOT_STARTED, TRANSITION_TABLE);
}
//--- Method(s) ---
protected synchronized void stateChanged(State oldState,
State newState)
{
if (newState == State.RUNNING) {
startAction.setEnabledLater(false);
stopAction.setEnabledLater(true);
deleteAction.setEnabledLater(false);
searchTask = new AutoSearchTask();
Scheduler.run
((getChildCount() > 0) ? INITIAL_SEARCH_INTERVAL : 0,
INITIAL_SEARCH_INTERVAL,
searchTask);
}
else if (newState == State.STOPPING) {
if (runningCount > 0) {
stopAllChildren();
stopAction.setEnabledLater(false);
}
else {
this.setState(State.NOT_STARTED);
}
}
else if (newState == State.SUCCEEDED
|| newState == State.NOT_STARTED) {
DownloadManager.getInstance().getQueue().remove
(OpenNapDownloadContainer.this);
stopAction.setEnabledLater(false);
deleteAction.setEnabledLater(true);
}
else if (newState == State.DELETED) {
OpenNapPlugin.getTransferManager().remove
(OpenNapDownloadContainer.this);
DownloadManager.getInstance().remove
(OpenNapDownloadContainer.this);
if (getFile() != null) {
getFile().delete();
}
}
if (newState == State.SUCCEEDED
|| newState == State.DELETED) {
OpenNapPlugin.getTransferManager().getResumeRepository().remove
(OpenNapDownloadContainer.this.data);
stopAllChildren();
}
else if (newState == State.NOT_STARTED) {
startAction.setEnabledLater(true);
}
if (oldState == State.RUNNING) {
searchTask.cancel();
searchTask = null;
}
}
}
/**
* Finds more sources regular intervals. */
private class AutoSearchTask extends XNapTask {
private long nextSearch = 0;
private int searchCount = 0;
private long searchInterval = INITIAL_SEARCH_INTERVAL;
public AutoSearchTask()
{
}
public void run()
{
if (!isAutoSearchingEnabled()) {
return;
}
if (nextSearch < System.currentTimeMillis()) {
searchForSources();
}
searchInterval *= 2;
if (searchInterval > MAX_SEARCH_INTERVAL) {
searchInterval = MAX_SEARCH_INTERVAL;
}
nextSearch = System.currentTimeMillis() + searchInterval;
if (++searchCount == MAX_SEARCH_COUNT) {
// notify user?
this.cancel();
}
}
}
private class AutoSearchAction extends AbstractToggleAction {
public AutoSearchAction()
{
putValue(Action.NAME, XNap.tr("Automatically search for more sources"));
}
public void toggled(boolean selected)
{
setAutoSearchingEnabled(selected);
}
}
private class DeleteAction extends AbstractDeleteAction {
public void actionPerformed(ActionEvent event)
{
// FIX: show confirmation dialog
try {
setState(State.DELETED);
}
catch (IllegalOperationException e) {
logger.warn("unexpected state");
}
}
}
private class EditAction extends AbstractEditAction {
public void actionPerformed(ActionEvent e)
{
OpenNapDownloadContainerEditorDialog.showDialog
(XNapFrame.getInstance(), OpenNapDownloadContainer.this);
}
}
private class FindMoreSourcesAction extends AbstractFindMoreSourcesAction {
public void actionPerformed(ActionEvent e)
{
searchForSources();
}
}
private class StartAction extends AbstractStartAction {
public void actionPerformed(ActionEvent e)
{
startTransfer();
}
}
private class StopAction extends AbstractStopAction {
public void actionPerformed(ActionEvent event)
{
stop();
}
}
private class StartRunner implements Runnable {
private OpenNapDownload d;
public StartRunner(OpenNapDownload d)
{
this.d = d;
}
public void run()
{
synchronized (sm) {
if (sm.getState() == State.RUNNING) {
d.start();
}
}
}
}
}
The table below shows all metrics for OpenNapDownloadContainer.java.



