/*******************************************************************************
* Copyright (c) 2007 The Eclipse Foundation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* The Eclipse Foundation - initial API and implementation
*******************************************************************************/
package org.eclipse.epp.usagedata.internal.recording.uploading;
import java.io.File;
import java.util.concurrent.locks.ReentrantLock;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Platform;
import org.eclipse.epp.usagedata.internal.recording.UsageDataRecordingActivator;
import org.eclipse.epp.usagedata.internal.recording.settings.UsageDataRecordingSettings;
import org.eclipse.epp.usagedata.internal.recording.uploading.codingspectator.TransferToCodingSpectatorListener;
import org.eclipse.ui.PlatformUI;
import edu.illinois.codingspectator.monitor.core.submission.SubmitterListener;
/**
*
* @author Mohsen Vakilian, nchen - Added check to see if we are only collecting and not uploading.
* And, added the support for transferring UDC data to CodingSpectator.
* @author Stas Negara - Merged former preLock and preSubmit methods into preSubmit.
*
*/
public class UploadManager implements SubmitterListener {
public static final int UPLOAD_STARTED_OK= 0;
public static final int NO_FILES_TO_UPLOAD= 1;
public static final int UPLOAD_IN_PROGRESS= 2;
public static final int WORKBENCH_IS_CLOSING= 3;
public static final int NO_UPLOADER= 4;
public static final int UPLOAD_DISABLED= 5;
//CODINGSPECTATOR
public static final int PREPARATION_OK= 6;
private Object lock= new Object();
private Uploader uploader;
private ListenerList uploadListeners= new ListenerList();
//CODINGSPECTATOR
private UploadParameters uploadParameters;
/**
* This method starts the upload. The first thing it does is find the files containing data that
* needs to be uploaded. If no data is found, then the method simply returns and the universe is
* left to unfold as it will. If data is found, we continue.
* <p>
* The settings are checked to see what the user wants us to do with the data. If the user has
* authorized that the data be uploaded, an upload job is spawned. If the settings indicate that
* the user must be asked what to do, then an editor is opened which invites the user to decide
* what to do with the information.
* </p>
* <p>
* This method returns a status code. The value is {@link #UPLOAD_IN_PROGRESS} if an upload is
* already in progress when the request is made, {@link #NO_FILES_TO_UPLOAD} if no files are
* available for upload, {@link #WORKBENCH_IS_CLOSING} if the workbench is closing at the time
* the request is made, {@link #NO_UPLOADER} if an uploader cannot be found, or
* {@value #UPLOAD_STARTED_OK} if a new upload is started.
* </p>
*
* @return a status code.
*/
public int startUpload() {
//CODINGSPECTATOR: Do not upload data if the user has chosen not to upload the data to Eclipse foundation.
if (getSettings().isCollectButNeverUpload()) {
return UPLOAD_DISABLED;
}
int preparationValue= prepareUploadData();
if (preparationValue != PREPARATION_OK)
return preparationValue;
/*
* Add a listener to the new uploader so that it will notify
* us when it is complete. Then, we'll notify our own listeners.
*/
uploader.addUploadListener(new UploadListener() {
public void uploadComplete(UploadResult result) {
uploader= null;
fireUploadComplete(result);
}
});
uploader.startUpload();
return UPLOAD_STARTED_OK;
}
//CODINGSPECTATOR: Extracted from startUpload.
public int prepareUploadData() {
if (!getSettings().isEnabled())
return UPLOAD_DISABLED;
if (PlatformUI.getWorkbench().isClosing())
return WORKBENCH_IS_CLOSING;
File[] usageDataUploadFiles;
synchronized (lock) {
if (uploader != null)
return UPLOAD_IN_PROGRESS;
usageDataUploadFiles= findUsageDataUploadFiles();
if (usageDataUploadFiles.length == 0)
return NO_FILES_TO_UPLOAD;
uploader= getUploader();
if (uploader == null)
return NO_UPLOADER;
}
getSettings().setLastUploadTime();
uploadParameters= new UploadParameters();
uploadParameters.setSettings(getSettings());
uploadParameters.setFiles(usageDataUploadFiles);
//request.setFilter(getSettings().getFilter());
uploader.setUploadParameters(uploadParameters);
/*
* Add a listener to the new uploader so that it will notify
* us when it is complete. Then, we'll notify our own listeners.
*/
uploader.addUploadListener(new UploadListener() {
public void uploadComplete(UploadResult result) {
uploader= null;
}
});
return PREPARATION_OK;
}
private File[] findUsageDataUploadFiles() {
return getSettings().getUsageDataUploadFiles();
}
private UsageDataRecordingSettings getSettings() {
return UsageDataRecordingActivator.getDefault().getSettings();
}
/**
* This method returns the {@link Uploader} to use to upload data to the server. At present,
* this is implemented using the extension point registry. This separation is done so that a UI
* component can participate in the upload process without muddying the lines between model and
* view; a UI plug-in, might, for example, open an editor or dialogue box asking the user if it
* is okay to do the upload.
* <p>
* An extension point is not quite the right fit here as there is currently only provision for
* there being a single choice. It's a good choice, because we want this to be lazy loaded. At
* some point, it may make sense to have multiple uploaders available and let the user (via
* preferences) select the one that they want to use (perhaps using a combo box or something.
* For now, that's probably just too complicated, and so the extension point is used with an
* understanding that there should only be one extension to it (in the event that there is more
* than one extension, the first one that we find is used).
* </p>
*
* @return
*/
private Uploader getUploader() {
IConfigurationElement[] elements= Platform.getExtensionRegistry()
.getConfigurationElementsFor(UsageDataRecordingActivator.PLUGIN_ID + ".uploader"); //$NON-NLS-1$
for (IConfigurationElement element : elements) {
if ("uploader".equals(element.getName())) { //$NON-NLS-1$
try {
Object uploader= element.createExecutableExtension("class"); //$NON-NLS-1$
if (uploader instanceof Uploader) {
return (Uploader)uploader;
}
} catch (CoreException e) {
UsageDataRecordingActivator.getDefault().getLog().log(e.getStatus());
}
}
}
return null;
}
public void addUploadListener(UploadListener listener) {
uploadListeners.add(listener);
}
public void removeUploadListener(UploadListener listener) {
uploadListeners.remove(listener);
}
protected void fireUploadComplete(UploadResult result) {
for (Object listener : uploadListeners.getListeners()) {
((UploadListener)listener).uploadComplete(result);
}
}
/////////////////
//CODINGSPECTATOR
/////////////////
/**
* This class uses the monitor.core.submitter extension point to make sure that transferring UDC
* data to the watched directory doesn't interfere with uploading the watched directory of
* CodingSpectator.
*
* The submitter and the UDC data transferrer should acquire the watched directory's lock in
* order to write into or upload the contents of the watched directory.
*/
public final static ReentrantLock watchedDirectoryLock= new ReentrantLock();
public int startTransferToCodingSpectator() {
int preparationValue= prepareUploadData();
if (preparationValue != PREPARATION_OK)
return preparationValue;
uploader.addTransferToCodingSpectatorListener(new TransferToCodingSpectatorListener() {
public void transferToCodingSpectatorComplete() {
uploader= null;
}
});
uploader.startTransferToCodingSpectator();
return UPLOAD_STARTED_OK;
}
public void preSubmit() {
int uploadResult= startTransferToCodingSpectator();
if (uploadResult == UPLOAD_DISABLED || uploadResult == WORKBENCH_IS_CLOSING || uploadResult == UPLOAD_IN_PROGRESS || uploadResult == NO_UPLOADER) {
UsageDataRecordingActivator.getDefault().log(IStatus.ERROR, String.format("Failed to transfer the UDC data into the watched folder (upload result = %d).", uploadResult));
}
watchedDirectoryLock.lock();
}
public void preCommit() {
}
public void postSubmit(boolean succeeded) {
try {
watchedDirectoryLock.unlock();
} catch (IllegalMonitorStateException e) {
UsageDataRecordingActivator.getDefault().log(IStatus.ERROR, e, "Failed to unlock the watched folder.");
}
}
}