blob: bd441ce8a0aa8d4c3deaf6beceee2bde9e8bfe87 [file] [log] [blame]
package org.eclipse.mdm.api.odsadapter.transaction;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.asam.ods.AoException;
import org.eclipse.mdm.api.base.FileService.ProgressListener;
import org.eclipse.mdm.api.base.model.Entity;
import org.eclipse.mdm.api.base.model.FileLink;
import org.eclipse.mdm.api.base.model.Value;
import org.eclipse.mdm.api.dflt.model.TemplateAttribute;
import org.eclipse.mdm.api.odsadapter.filetransfer.CORBAFileService;
import org.eclipse.mdm.api.odsadapter.filetransfer.Transfer;
import org.eclipse.mdm.api.odsadapter.query.ODSModelManager;
/**
* Manages new or removed externally linked files.
*
* @since 1.0.0
* @author Viktor Stoehr, Gigatronik Ingolstadt GmbH
*/
final class UploadService {
// ======================================================================
// Instance variables
// ======================================================================
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final Map<TemplateAttribute, Value> templateAttributeFileLinks = new HashMap<>();
private final List<FileLink> uploaded = new ArrayList<>();
private final Map<Path, String> remotePaths = new HashMap<>();
private final List<FileLink> toRemove = new ArrayList<>();
private final CORBAFileService fileService;
private final Entity entity;
// ======================================================================
// Constructors
// ======================================================================
/**
* Constructor.
*
* @param modelManager
* Used for setup.
* @param entity
* Used for security checks.
* @param transfer
* The transfer type.
*/
UploadService(ODSModelManager modelManager, Entity entity, Transfer transfer) {
fileService = new CORBAFileService(modelManager, transfer);
this.entity = entity;
scheduler.scheduleAtFixedRate(() -> {
try {
modelManager.getAoSession().getName();
} catch (AoException e) {
/*
* NOTE: This is done to keep the parent transaction's session
* alive till its commit or abort method is called. If this
* session refresh results in an error, then any running file
* transfer will abort with a proper error, therefore any
* exception here is completely ignored and explicitly NOT
* logged!
*/
}
}, 5, 5, TimeUnit.MINUTES);
}
// ======================================================================
// Public methods
// ======================================================================
/**
* Uploads new externally linked files stored in given
* {@link TemplateAttribute}s. The upload progress may be traced with a
* progress listener.
*
* @param templateAttributes
* The {@link TemplateAttribute}s.
* @param progressListener
* The progress listener.
* @throws IOException
* Thrown if unable to upload files.
*/
public void upload(Collection<TemplateAttribute> templateAttributes, ProgressListener progressListener)
throws IOException {
List<FileLink> fileLinks = new ArrayList<>();
for (TemplateAttribute templateAttribute : templateAttributes) {
Value defaultValue = templateAttribute.getDefaultValue();
if (!defaultValue.isValid()) {
continue;
}
if (defaultValue.getValueType().isFileLink()) {
fileLinks.add(defaultValue.extract());
} else if (defaultValue.getValueType().isFileLinkSequence()) {
fileLinks.addAll(Arrays.asList((FileLink[]) defaultValue.extract()));
} else {
throw new IllegalStateException("Template attribute's value type is not of type file link.");
}
templateAttributeFileLinks.put(templateAttribute, defaultValue);
}
if (!fileLinks.isEmpty()) {
uploadParallel(fileLinks, progressListener);
// remote paths available -> update template attribute
templateAttributeFileLinks.forEach((ta, v) -> ta.setDefaultValue(v.extract()));
}
}
/**
* Parallel upload of given {@link FileLink}s. Local {@link Path}s linked
* multiple times are uploaded only once. The upload progress may be traced
* with a progress listener.
*
* @param fileLinks
* Collection of {@code FileLink}s to upload.
* @param progressListener
* The progress listener.
* @throws IOException
* Thrown if unable to upload files.
*/
public void uploadParallel(Collection<FileLink> fileLinks, ProgressListener progressListener) throws IOException {
List<FileLink> filtered = retainForUpload(fileLinks);
try {
fileService.uploadParallel(entity, filtered, progressListener);
} finally {
filtered.stream().filter(FileLink::isRemote).forEach(fl -> {
remotePaths.put(fl.getLocalPath(), fl.getRemotePath());
uploaded.add(fl);
});
}
}
/**
* Once {@link #commit()} is called given {@link FileLink}s will be deleted
* from the remote storage.
*
* @param fileLinks
* Collection of {@code FileLink}s to delete.
*/
public void addToRemove(Collection<FileLink> fileLinks) {
toRemove.addAll(fileLinks);
}
/**
* Commits modifications of externally linked files.
*/
public void commit() {
fileService.delete(entity, toRemove);
scheduler.shutdown();
}
/**
* Aborts modifications of externally linked files.
*/
public void abort() {
fileService.delete(entity, uploaded);
uploaded.forEach(fl -> fl.setRemotePath(null));
templateAttributeFileLinks.forEach((ta, v) -> ta.setDefaultValue(v.extract()));
scheduler.shutdown();
}
// ======================================================================
// Private methods
// ======================================================================
/**
* Filters given {@link FileLink}s by removing already uploaded ones.
*
* @param fileLinks
* Will be filtered.
* @return Returns {@code FileLink}s which have to be uploaded.
*/
private List<FileLink> retainForUpload(Collection<FileLink> fileLinks) {
List<FileLink> filtered = new ArrayList<>(fileLinks);
for (FileLink fileLink : fileLinks) {
String remotePath = remotePaths.get(fileLink.getLocalPath());
if (remotePath != null && !remotePath.isEmpty()) {
fileLink.setRemotePath(remotePath);
filtered.remove(fileLink);
uploaded.add(fileLink);
}
}
return filtered;
}
}