package rocks.inspectit.ui.rcp.storage; import java.io.IOException; import java.io.InputStream; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.apache.commons.lang.mutable.MutableObject; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.swt.widgets.Display; import com.esotericsoftware.kryo.io.Input; import rocks.inspectit.shared.all.cmr.model.PlatformIdent; import rocks.inspectit.shared.all.communication.DefaultData; import rocks.inspectit.shared.all.communication.data.cmr.BusinessTransactionData; import rocks.inspectit.shared.all.exception.BusinessException; import rocks.inspectit.shared.all.exception.enumeration.StorageErrorCodeEnum; import rocks.inspectit.shared.all.serializer.ISerializer; import rocks.inspectit.shared.all.serializer.SerializationException; import rocks.inspectit.shared.all.util.ObjectUtils; import rocks.inspectit.shared.cs.indexing.storage.IStorageTreeComponent; import rocks.inspectit.shared.cs.indexing.storage.impl.ArrayBasedStorageLeaf; import rocks.inspectit.shared.cs.indexing.storage.impl.CombinedStorageBranch; import rocks.inspectit.shared.cs.storage.IStorageData; import rocks.inspectit.shared.cs.storage.LocalStorageData; import rocks.inspectit.shared.cs.storage.StorageData; import rocks.inspectit.shared.cs.storage.StorageFileType; import rocks.inspectit.shared.cs.storage.StorageManager; import rocks.inspectit.shared.cs.storage.label.StringStorageLabel; import rocks.inspectit.shared.cs.storage.label.type.impl.ExploredByLabelType; import rocks.inspectit.ui.rcp.InspectIT; import rocks.inspectit.ui.rcp.repository.CmrRepositoryChangeListener; import rocks.inspectit.ui.rcp.repository.CmrRepositoryDefinition; import rocks.inspectit.ui.rcp.repository.CmrRepositoryDefinition.OnlineStatus; import rocks.inspectit.ui.rcp.repository.StorageRepositoryDefinition; import rocks.inspectit.ui.rcp.repository.StorageRepositoryDefinitionProvider; import rocks.inspectit.ui.rcp.storage.listener.StorageChangeListener; import rocks.inspectit.ui.rcp.storage.util.DataRetriever; import rocks.inspectit.ui.rcp.storage.util.DataUploader; /** * {@link StorageManager} for GUI. * * @author Ivan Senic * */ public class InspectITStorageManager extends StorageManager implements CmrRepositoryChangeListener { // NOPMD /** * List of downloaded storages. */ private final Set<LocalStorageData> downloadedStorages = Collections.newSetFromMap(new ConcurrentHashMap<LocalStorageData, Boolean>(16, 0.75f, 2)); /** * Map of mounted and online not available storages. */ private final Set<LocalStorageData> mountedNotAvailableStorages = Collections.newSetFromMap(new ConcurrentHashMap<LocalStorageData, Boolean>(16, 0.75f, 2)); /** * Map of mounted and online not available storages. */ private Map<LocalStorageData, CmrRepositoryDefinition> mountedAvailableStorages = new ConcurrentHashMap<>(16, 0.75f, 2); /** * Cashed statuses of CMR repository definitions. */ private ConcurrentHashMap<CmrRepositoryDefinition, OnlineStatus> cachedRepositoriesStatus = new ConcurrentHashMap<>(16, 0.75f, 2); /** * List of {@link StorageChangeListener}s. */ private List<StorageChangeListener> storageChangeListeners = new ArrayList<>(); /** * {@link DataRetriever}. */ private DataRetriever dataRetriever; /** * {@link DataUploader}. */ private DataUploader dataUploader; /** * {@link StorageRepositoryDefinitionProvider}. */ private StorageRepositoryDefinitionProvider storageRepositoryDefinitionProvider; /** * Mounts a new storage locally. Same as calling {@link #mountStorage(StorageData, * CmrRepositoryDefinition, false, false)}. * * @param storageData * Storage to mount. * @param cmrRepositoryDefinition * {@link CmrRepositoryDefinition}. * @param subMonitor * {@link SubMonitor} to report to. * @throws BusinessException * If storage directory can not be created or storage file can not be saved. * @throws IOException * If {@link IOException} occurs. * @throws SerializationException * If {@link SerializationException} occurs. */ public void mountStorage(StorageData storageData, CmrRepositoryDefinition cmrRepositoryDefinition, SubMonitor subMonitor) throws BusinessException, IOException, SerializationException { this.mountStorage(storageData, cmrRepositoryDefinition, false, false, subMonitor); } /** * Mounts a new storage locally. Provides option to specify if the complete download should be * performed. * * @param storageData * Storage to mount. * @param cmrRepositoryDefinition * {@link CmrRepositoryDefinition}. * @param fullyDownload * Should storage be immediately fully down-loaded. Intended for future use. * @param compressBefore * If the fullyDownalod is <code>true</code>, this parameter can define if data files * should be compressed on the fly before sent. * @param subMonitor * {@link SubMonitor} to report to. * @throws BusinessException * If storage directory can not be created or storage file can not be saved. * @throws IOException * If {@link IOException} occurs. * @throws SerializationException * If {@link SerializationException} occurs. * */ private void mountStorage(StorageData storageData, CmrRepositoryDefinition cmrRepositoryDefinition, boolean fullyDownload, boolean compressBefore, SubMonitor subMonitor) throws BusinessException, IOException, SerializationException { LocalStorageData localStorageData = new LocalStorageData(storageData); Path directory = getStoragePath(localStorageData); if (!Files.exists(directory)) { Files.createDirectories(directory); } if (fullyDownload) { try { subMonitor.setTaskName("Downloading storage files for storage '" + storageData.getName() + "'.."); dataRetriever.downloadAndSaveStorageFiles(cmrRepositoryDefinition, storageData, directory, compressBefore, true, subMonitor, StorageFileType.values()); downloadedStorages.add(localStorageData); localStorageData.setFullyDownloaded(true); } catch (Exception e) { deleteLocalStorageData(localStorageData, false); throw e; } } else { try { subMonitor.setTaskName("Downloading agent and indexing files for storage '" + storageData.getName() + "'.."); dataRetriever.downloadAndSaveStorageFiles(cmrRepositoryDefinition, storageData, directory, compressBefore, true, subMonitor, StorageFileType.AGENT_FILE, StorageFileType.INDEX_FILE, StorageFileType.BUSINESS_CONTEXT_FILE); } catch (Exception e) { deleteLocalStorageData(localStorageData, false); throw e; } } writeLocalStorageDataToDisk(localStorageData); final String systemUserName = getSystemUsername(); try { if (null != systemUserName) { StringStorageLabel mountedByLabel = new StringStorageLabel(systemUserName, new ExploredByLabelType()); cmrRepositoryDefinition.getStorageService().addLabelToStorage(storageData, mountedByLabel, true); } } catch (final Exception e) { Display.getDefault().syncExec(new Runnable() { @Override public void run() { InspectIT.getDefault().createInfoDialog("'Mounted by' label with value '" + systemUserName + "'was not added to the storage. Exception message is:\n\n" + e.getMessage(), -1); } }); } mountedAvailableStorages.put(localStorageData, cmrRepositoryDefinition); } /** * Returns if the storage data is already downloaded. * * @param storageData * {@link StorageData}. * @return Returns if the storage data is already downloaded. */ public boolean isFullyDownloaded(StorageData storageData) { for (LocalStorageData lsd : downloadedStorages) { if (ObjectUtils.equals(lsd.getId(), storageData.getId())) { return lsd.isFullyDownloaded(); } } return false; } /** * Fully downloads selected storage. * * @param storageData * StorageData. * @param cmrRepositoryDefinition * {@link CmrRepositoryDefinition}. * @param compressBefore * Should data files be compressed on the fly before sent. * @param subMonitor * {@link SubMonitor} to report to. * @throws SerializationException * If serialization exception occurs. * @throws IOException * If {@link IOException} occurs. * @throws BusinessException * If storage is already fully downloaded. */ public void fullyDownloadStorage(StorageData storageData, CmrRepositoryDefinition cmrRepositoryDefinition, boolean compressBefore, SubMonitor subMonitor) throws BusinessException, IOException, SerializationException { LocalStorageData localStorageData = null; for (LocalStorageData lsd : mountedAvailableStorages.keySet()) { if (ObjectUtils.equals(lsd.getId(), storageData.getId())) { localStorageData = lsd; break; } } if (null == localStorageData) { mountStorage(storageData, cmrRepositoryDefinition, true, compressBefore, subMonitor); return; } if (localStorageData.isFullyDownloaded()) { throw new BusinessException("Download the storage " + storageData + ".", StorageErrorCodeEnum.STORAGE_ALREADY_DOWNLOADED); } Path directory = getStoragePath(localStorageData); subMonitor.setTaskName("Downloading storage data files for storage '" + storageData.getName() + "'.."); dataRetriever.downloadAndSaveStorageFiles(cmrRepositoryDefinition, storageData, directory, compressBefore, true, subMonitor, StorageFileType.DATA_FILE, StorageFileType.CACHED_DATA_FILE); downloadedStorages.add(localStorageData); localStorageData.setFullyDownloaded(true); writeLocalStorageDataToDisk(localStorageData); } /** * Deletes all local data saved for given {@link LocalStorageData}, unmount storage. * * @param localStorageData * {@link LocalStorageData}. * @throws IOException * If deleting of the local data fails. * @throws SerializationException * If serialization fails. */ public void deleteLocalStorageData(LocalStorageData localStorageData) throws IOException, SerializationException { this.deleteLocalStorageData(localStorageData, true); } /** * Deletes all local data saved for given {@link LocalStorageData}, unmount storage. * * @param localStorageData * {@link LocalStorageData}. * @param informListeners * Should the listeners be informed. * @throws IOException * If deleting of the local data fails. * @throws SerializationException * If serialization fails. */ private void deleteLocalStorageData(LocalStorageData localStorageData, boolean informListeners) throws IOException, SerializationException { localStorageData.setFullyDownloaded(false); downloadedStorages.remove(localStorageData); if (mountedAvailableStorages.containsKey(localStorageData) || mountedNotAvailableStorages.contains(localStorageData)) { super.deleteStorageDataFromDisk(localStorageData, StorageFileType.DATA_FILE); writeLocalStorageDataToDisk(localStorageData); } else { super.deleteCompleteStorageDataFromDisk(localStorageData); } if (informListeners) { synchronized (storageChangeListeners) { for (StorageChangeListener storageChangeListener : storageChangeListeners) { storageChangeListener.storageLocallyDeleted(localStorageData); } } } } /** * Informs the {@link InspectITStorageManager} that a {@link StorageData} has been remotely * deleted. * * @param storageData * {@link StorageData}. * @throws SerializationException * If serialization fails. * @throws IOException * If {@link IOException} occurs. */ public void storageRemotelyDeleted(StorageData storageData) throws IOException, SerializationException { LocalStorageData localStorageData = null; for (Map.Entry<LocalStorageData, CmrRepositoryDefinition> entry : mountedAvailableStorages.entrySet()) { if (ObjectUtils.equals(entry.getKey().getId(), storageData.getId())) { localStorageData = entry.getKey(); break; } } if ((null != localStorageData) && !localStorageData.isFullyDownloaded()) { deleteLocalStorageData(localStorageData, false); } else { for (LocalStorageData notAvailable : mountedNotAvailableStorages) { if (ObjectUtils.equals(notAvailable.getId(), storageData.getId())) { localStorageData = notAvailable; break; } } if ((null != localStorageData) && !localStorageData.isFullyDownloaded()) { deleteLocalStorageData(localStorageData, false); } } synchronized (storageChangeListeners) { for (StorageChangeListener storageChangeListener : storageChangeListeners) { storageChangeListener.storageRemotelyDeleted(storageData); } } } /** * Informs the Storage Manager that the {@link StorageData} has been remotely updated, so that * existing local clone of the data can be updated. * * @param storageData * {@link StorageData} that was updated. * @throws IOException * If {@link IOException} occurs during saving data to disk. * @throws SerializationException * If serialization fails during saving data to disk. */ public void storageRemotelyUpdated(StorageData storageData) throws IOException, SerializationException { LocalStorageData localStorageData = getLocalDataForStorage(storageData); if (null != localStorageData) { updateLocalStorageData(localStorageData, storageData); } synchronized (storageChangeListeners) { for (StorageChangeListener storageChangeListener : storageChangeListeners) { storageChangeListener.storageDataUpdated(storageData); } } } /** * Returns mounted and available storages, thus the ones that can be read from. * * @return List of {@link LocalStorageData}. */ public Collection<LocalStorageData> getMountedAvailableStorages() { return Collections.unmodifiableSet(mountedAvailableStorages.keySet()); } /** * Returns mounted but not available storages, thus the ones that can not be read from because * there is no CMR that can handle them. * * @return List of {@link LocalStorageData}. */ public Collection<LocalStorageData> getMountedUnavailableStorages() { return Collections.unmodifiableSet(mountedNotAvailableStorages); } /** * Returns collection of fully downloaded storages. * * @return List of {@link LocalStorageData}. */ public Collection<LocalStorageData> getDownloadedStorages() { return Collections.unmodifiableSet(downloadedStorages); } /** * Returns storage {@link Path}. * * @param storageData * Storage. * @return Returns storage {@link Path}. * @see Paths#get(String, String...) */ @Override public Path getStoragePath(IStorageData storageData) { return getDefaultStorageDirPath().resolve(storageData.getStorageFolder()); } /** * Loads initial local mounted storage information. */ public void startUp() { List<LocalStorageData> mountedStorages; try { mountedStorages = getMountedStoragesFromDisk(); } catch (Exception e) { mountedStorages = Collections.emptyList(); } Map<StorageData, CmrRepositoryDefinition> onlineStorages = getOnlineStorages(); for (LocalStorageData localStorageData : mountedStorages) { if (localStorageData.isFullyDownloaded()) { downloadedStorages.add(localStorageData); } boolean availableOnline = false; for (Map.Entry<StorageData, CmrRepositoryDefinition> entry : onlineStorages.entrySet()) { if (ObjectUtils.equals(entry.getKey().getId(), localStorageData.getId())) { availableOnline = true; try { updateLocalStorageData(localStorageData, entry.getKey()); } catch (final Exception e) { final String name = localStorageData.getName(); Display.getDefault().syncExec(new Runnable() { @Override public void run() { InspectIT.getDefault().createErrorDialog("Local data for the storage '" + name + "' could not be updated with the online data.", e, -1); } }); } mountedAvailableStorages.put(localStorageData, entry.getValue()); break; } } if (!availableOnline) { mountedNotAvailableStorages.add(localStorageData); } } InspectIT.getDefault().getCmrRepositoryManager().addCmrRepositoryChangeListener(this); } /** * Instantiates the new storage repository definition based on the {@link LocalStorageData} * provided. Note that the local storage data has to be in the collection of * {@link #getMountedAvailableStorages()}. Otherwise the creation will fail with an Exception * being thrown.. * <p> * A special care needs to be taken with the method, because the the returned object could be * quite big, since it will hold the complete indexing tree. Thus, it is important that the * caller of this method take responsibility to make earlier created definitions ready for * garbage collection as soon as they are not needed anymore. * * @param localStorageData * {@link LocalStorageData} to create the definition for. * @return {@link StorageRepositoryDefinition}. * @throws BusinessException * If the wanted {@link LocalStorageData} is not available. * @throws SerializationException * If {@link SerializationException} occurs. * @throws IOException * If {@link IOException} occurs. * */ public StorageRepositoryDefinition getStorageRepositoryDefinition(LocalStorageData localStorageData) throws BusinessException, SerializationException, IOException { // check if it is available if (!mountedAvailableStorages.keySet().contains(localStorageData) && !downloadedStorages.contains(localStorageData)) { throw new BusinessException("Create repository definition for the storage " + localStorageData + ".", StorageErrorCodeEnum.STORAGE_DOES_NOT_EXIST); } // find CMR repository def, allow null for downloaded and not mounted storages CmrRepositoryDefinition cmrRepositoryDefinition = null; cmrRepositoryDefinition = mountedAvailableStorages.get(localStorageData); // get agents List<PlatformIdent> platformIdents = getPlatformIdentsLocally(localStorageData); if (null == platformIdents) { platformIdents = Collections.emptyList(); } // get indexing tree IStorageTreeComponent<? extends DefaultData> indexingTree = getIndexingTree(localStorageData); if (null == indexingTree) { indexingTree = new ArrayBasedStorageLeaf<>(); } // get business context Collection<BusinessTransactionData> businessTransactions = getBusinessContextLocally(localStorageData); // create new storage repository definition StorageRepositoryDefinition storageRepositoryDefinition = storageRepositoryDefinitionProvider.createStorageRepositoryDefinition(); storageRepositoryDefinition.setAgents(platformIdents); storageRepositoryDefinition.setBusinessTransactions(businessTransactions); storageRepositoryDefinition.setIndexingTree(indexingTree); storageRepositoryDefinition.setCmrRepositoryDefinition(cmrRepositoryDefinition); storageRepositoryDefinition.setLocalStorageData(localStorageData); storageRepositoryDefinition.initServices(); return storageRepositoryDefinition; } /** * Checks if the storage is locally mounted. * * @param storageData * Storage data to check. * @return True if storage is mounted, false otherwise. */ public boolean isStorageMounted(StorageData storageData) { return getLocalDataForStorage(storageData) != null; } /** * Returns the local data for storage if the storage is mounted or downloaded. * * @param storageData * Storage data to check. * @return {@link LocalStorageData}. */ public LocalStorageData getLocalDataForStorage(StorageData storageData) { for (LocalStorageData downloadedStorage : downloadedStorages) { if (ObjectUtils.equals(downloadedStorage.getId(), storageData.getId())) { return downloadedStorage; } } for (LocalStorageData mountedStorage : mountedNotAvailableStorages) { if (ObjectUtils.equals(storageData.getId(), mountedStorage.getId())) { return mountedStorage; } } for (LocalStorageData mountedStorage : mountedAvailableStorages.keySet()) { if (ObjectUtils.equals(storageData.getId(), mountedStorage.getId())) { return mountedStorage; } } return null; } /** * Registers a {@link StorageChangeListener} if the same listener does not already exist. * * @param storageChangeListener * {@link StorageChangeListener} to add. */ public void addStorageChangeListener(StorageChangeListener storageChangeListener) { synchronized (storageChangeListeners) { if (!storageChangeListeners.contains(storageChangeListener)) { storageChangeListeners.add(storageChangeListener); } } } /** * Removes a {@link StorageChangeListener}. * * @param storageChangeListener * {@link StorageChangeListener} to remove. */ public void removeStorageChangeListener(StorageChangeListener storageChangeListener) { synchronized (storageChangeListeners) { storageChangeListeners.remove(storageChangeListener); } } /** * Uploads a file to the {@link CmrRepositoryDefinition} storage uploads. * * @param fileName * Name of file. * @param cmrRepositoryDefinition * {@link CmrRepositoryDefinition}. * @param subMonitor * {@link SubMonitor} to report progress to. * @throws IOException * If upload file does not exist or upload fails. */ public void uploadZippedStorage(String fileName, CmrRepositoryDefinition cmrRepositoryDefinition, SubMonitor subMonitor) throws IOException { Path file = Paths.get(fileName); Path relativizePath = file.getParent(); String tmpDir = "tmp" + UUID.randomUUID().hashCode(); subMonitor.setTaskName("Uploading storage file.."); // no compressing since it is already zipped dataUploader.uploadFileToStorageUploads(file, relativizePath, tmpDir, cmrRepositoryDefinition, subMonitor); } /** * Uploads a complete storage to the {@link CmrRepositoryDefinition} upload folder. All files * belonging to the local storage data will be uploaded to the temporary directory. * * @param localStorageData * Storage to upload. * @param cmrRepositoryDefinition * Repository definition. * @param subMonitor * The monitor to report upload progress to. * @throws BusinessException * If storage is not fully downloaded. * @throws IOException * If {@link IOException} occurss during operation. */ public void uploadCompleteStorage(LocalStorageData localStorageData, final CmrRepositoryDefinition cmrRepositoryDefinition, SubMonitor subMonitor) throws BusinessException, IOException { if (!localStorageData.isFullyDownloaded()) { throw new BusinessException("Uploading the storage " + localStorageData + ".", StorageErrorCodeEnum.STORAGE_IS_NOT_DOWNLOADED); } String tmpDir = "tmp" + UUID.randomUUID().hashCode(); Path storageDir = getStoragePath(localStorageData); final List<Path> toUpload = new ArrayList<>(); Files.walkFileTree(storageDir, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { toUpload.add(file); return FileVisitResult.CONTINUE; } }); subMonitor.setTaskName("Uploading storage files.."); dataUploader.uploadFileToStorageUploads(toUpload, storageDir, tmpDir, cmrRepositoryDefinition, subMonitor); } /** * Compresses the content of the local storage data folder to the file. File name is provided * via given path. If the file already exists, it will be deleted first. * * @param localStorageData * {@link LocalStorageData} to zip. * @param zipFileName * Zip file name. * @throws BusinessException * If the storage is not fully downloaded. * @throws IOException * If {@link IOException} occurs during compressing. */ public void zipStorageData(LocalStorageData localStorageData, String zipFileName) throws BusinessException, IOException { if (!localStorageData.isFullyDownloaded()) { throw new BusinessException("Zip data for the storage " + localStorageData + ".", StorageErrorCodeEnum.STORAGE_IS_NOT_DOWNLOADED); } else { Path zipPath = Paths.get(zipFileName); if (Files.exists(zipPath)) { Files.delete(zipPath); } try { super.zipStorageData(localStorageData, zipPath); } catch (IOException e) { Files.deleteIfExists(zipPath); throw e; } } } /** * Zips the remote storage files to the file. File name is provided via given path. If the file * already exists, it will be deleted first. * * @param storageData * Remote storage to zip. * @param cmrRepositoryDefinition * {@link CmrRepositoryDefinition} where storage is located. * @param zipFileName * Zip file name. * @param compressBefore * Defines if the data should be compressed before downloading. * @param subMonitor * {@link SubMonitor} to report to. * @throws BusinessException * If serialization of data fails during zipping. * @throws IOException * If {@link IOException} occurs during compressing. * @throws SerializationException * If {@link SerializationException} occurs. */ public void zipStorageData(StorageData storageData, CmrRepositoryDefinition cmrRepositoryDefinition, String zipFileName, boolean compressBefore, SubMonitor subMonitor) throws BusinessException, IOException, SerializationException { Path zipPath = Paths.get(zipFileName); if (Files.exists(zipPath)) { Files.delete(zipPath); } try (final ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zipPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE))) { // download and pack at the same time subMonitor.setTaskName("Downloading and packing storage files.."); dataRetriever.downloadAndZipStorageFiles(cmrRepositoryDefinition, storageData, zos, compressBefore, false, subMonitor, StorageFileType.values()); // add local storage data info LocalStorageData localStorageData = new LocalStorageData(storageData); localStorageData.setFullyDownloaded(true); String fileName = localStorageData.getId() + StorageFileType.LOCAL_STORAGE_FILE.getExtension(); ZipEntry zipEntry = new ZipEntry(fileName); zos.putNextEntry(zipEntry); serializeDataToOutputStream(localStorageData, zos, false); zos.closeEntry(); } catch (IOException | BusinessException | SerializationException e) { Files.deleteIfExists(zipPath); throw e; } } /** * Returns the {@link StorageData} object that exists in the compressed storage file. * * @param zipFileName * Compressed storage file name. * @return {@link IStorageData} object or <code>null</code> if the given file is not of correct * type or does not exist. */ public IStorageData getStorageDataFromZip(String zipFileName) { return this.getStorageDataFromZip(Paths.get(zipFileName)); } /** * Unzips the content of the zip file provided to the default storage folder. The method will * first unzip the complete content of the zip file to the temporary folder and then rename the * temporary folder to match the storage ID. * <p> * The method will also check if the imported storage is available online, and if it is will * update the local data saved. * * @param fileName * File to unzip. * @throws BusinessException * If given file does not exist or content is not proper. * @throws IOException * If {@link IOException} occurs. * @throws SerializationException * If serialization exception occurs if data needs to be updated. * */ public void unzipStorageData(String fileName) throws BusinessException, IOException, SerializationException { Path zipPath = Paths.get(fileName); IStorageData packedStorageData = getStorageDataFromZip(zipPath); this.unzipStorageData(zipPath, getStoragePath(packedStorageData)); List<LocalStorageData> localStorageDataList = getMountedStoragesFromDisk(); for (LocalStorageData localStorageData : localStorageDataList) { if (localStorageData.isFullyDownloaded() && !downloadedStorages.contains(localStorageData)) { downloadedStorages.add(localStorageData); for (StorageData storageData : getOnlineStorages().keySet()) { if (ObjectUtils.equals(storageData.getId(), localStorageData.getId())) { updateLocalStorageData(localStorageData, storageData); break; } } break; } } } /** * {@inheritDoc} */ @Override public void repositoryAdded(CmrRepositoryDefinition cmrRepositoryDefinition) { this.addMountedStorages(cmrRepositoryDefinition); } /** * {@inheritDoc} */ @Override public void repositoryRemoved(CmrRepositoryDefinition cmrRepositoryDefinition) { this.removeMountedStorages(cmrRepositoryDefinition); } /** * {@inheritDoc} */ @Override public void repositoryDataUpdated(CmrRepositoryDefinition cmrRepositoryDefinition) { } /** * {@inheritDoc} */ @Override public void repositoryAgentDeleted(CmrRepositoryDefinition cmrRepositoryDefinition, PlatformIdent agent) { } /** * {@inheritDoc} */ @Override public void repositoryOnlineStatusUpdated(CmrRepositoryDefinition cmrRepositoryDefinition, OnlineStatus oldStatus, OnlineStatus newStatus) { if (newStatus != OnlineStatus.CHECKING) { OnlineStatus cachedStatus = cachedRepositoriesStatus.get(cmrRepositoryDefinition); if (!ObjectUtils.equals(cachedStatus, newStatus)) { if (newStatus == OnlineStatus.ONLINE) { this.addMountedStorages(cmrRepositoryDefinition); } else if (newStatus == OnlineStatus.OFFLINE) { this.removeMountedStorages(cmrRepositoryDefinition); } } cachedRepositoriesStatus.put(cmrRepositoryDefinition, newStatus); } } /** * Updates the information of the local storage data saved on the client machine with the data * provided in the storage data available online. * * @param localStorageData * Local storage data to update. * @param storageData * Storage data that holds new information. * @throws SerializationException * If serialization of data fails. * @throws IOException * If {@link IOException} occurs during data saving to disk. */ private void updateLocalStorageData(LocalStorageData localStorageData, StorageData storageData) throws IOException, SerializationException { localStorageData.copyStorageDataInformation(storageData); writeLocalStorageDataToDisk(localStorageData); } /** * Adds mounted storages that are bounded to the given {@link CmrRepositoryDefinition} to * "available" map. * * @param cmrRepositoryDefinition * {@link CmrRepositoryDefinition}. */ private void addMountedStorages(CmrRepositoryDefinition cmrRepositoryDefinition) { if (cmrRepositoryDefinition.getOnlineStatus() == OnlineStatus.ONLINE) { List<StorageData> closedStorages = cmrRepositoryDefinition.getStorageService().getReadableStorages(); List<LocalStorageData> newAvailableStoarges = new ArrayList<>(); for (LocalStorageData localStorageData : mountedNotAvailableStorages) { for (StorageData storageData : closedStorages) { if (ObjectUtils.equals(localStorageData.getId(), storageData.getId())) { newAvailableStoarges.add(localStorageData); try { updateLocalStorageData(localStorageData, storageData); } catch (final SerializationException | IOException e) { final String name = localStorageData.getName(); Display.getDefault().syncExec(new Runnable() { @Override public void run() { InspectIT.getDefault().createErrorDialog("Local data for the storage '" + name + "' could not be updated with the online data.", e, -1); } }); } mountedAvailableStorages.put(localStorageData, cmrRepositoryDefinition); break; } } } if (!newAvailableStoarges.isEmpty()) { mountedNotAvailableStorages.removeAll(newAvailableStoarges); } } } /** * Removes mounted storages that are bounded to the given {@link CmrRepositoryDefinition} to * "available" map. * * @param cmrRepositoryDefinition * {@link CmrRepositoryDefinition}. */ private void removeMountedStorages(CmrRepositoryDefinition cmrRepositoryDefinition) { List<LocalStorageData> removeList = new ArrayList<>(); for (Map.Entry<LocalStorageData, CmrRepositoryDefinition> entry : mountedAvailableStorages.entrySet()) { if (!entry.getKey().isFullyDownloaded()) { if (ObjectUtils.equals(entry.getValue(), cmrRepositoryDefinition)) { removeList.add(entry.getKey()); } } } if (!removeList.isEmpty()) { mountedAvailableStorages.keySet().removeAll(removeList); mountedNotAvailableStorages.addAll(removeList); } } /** * Loads {@link PlatformIdent}s from a disk for a storage. * * @param storageData * {@link IStorageData} * @return List of {@link PlatformIdent}s involved in the storage data or null if no file * exists. * @throws IOException * If {@link IOException} occurs. * @throws SerializationException * If data can not be deserialized. */ private List<PlatformIdent> getPlatformIdentsLocally(final IStorageData storageData) throws IOException, SerializationException { Path storagePath = getStoragePath(storageData); List<PlatformIdent> returnList = this.getObjectsByFileTreeWalk(storagePath, StorageFileType.AGENT_FILE.getExtension()); if (!returnList.isEmpty()) { return returnList; } else { return null; } } /** * Loads {@link IBusinessContextDefinition} from local storage. * * @param storageData * the storage data defining the storage to load the * {@link IBusinessContextDefinition} from * @return {@link IBusinessContextDefinition} instance * @throws IOException * if loading fails * @throws SerializationException * if loading fails */ private Set<BusinessTransactionData> getBusinessContextLocally(final IStorageData storageData) throws IOException, SerializationException { Path storagePath = getStoragePath(storageData); List<Collection<BusinessTransactionData>> tmpResult = this.getObjectsByFileTreeWalk(storagePath, StorageFileType.BUSINESS_CONTEXT_FILE.getExtension()); Set<BusinessTransactionData> realResult = new HashSet<>(); for (Collection<BusinessTransactionData> collection : tmpResult) { realResult.addAll(collection); } return realResult; } /** * Loads indexing tree from a disk for a storage. * * @param storageData * {@link IStorageData} * @return Indexing tree or null if it can not be found. * @throws IOException * If {@link IOException} occurs. * @throws SerializationException * If data can not be deserialized. */ private IStorageTreeComponent<DefaultData> getIndexingTree(final IStorageData storageData) throws IOException, SerializationException { Path storagePath = getStoragePath(storageData); List<IStorageTreeComponent<DefaultData>> indexingTrees = this.getObjectsByFileTreeWalk(storagePath, StorageFileType.INDEX_FILE.getExtension()); if (!indexingTrees.isEmpty()) { if (indexingTrees.size() == 1) { return indexingTrees.get(0); } else { CombinedStorageBranch<DefaultData> combinedStorageBranch = new CombinedStorageBranch<>(indexingTrees); return combinedStorageBranch; } } else { return null; } } /** * Returns all storages that have been mounted locally. * * @return Returns all storages that have been mounted locally as a list of * {@link LocalStorageData}. * @throws IOException * If {@link IOException} occurs. * @throws SerializationException * If data can not be deserialized. */ private List<LocalStorageData> getMountedStoragesFromDisk() throws IOException, SerializationException { return this.getObjectsByFileTreeWalk(getDefaultStorageDirPath(), StorageFileType.LOCAL_STORAGE_FILE.getExtension()); } /** * Reads the objects from files that are in a given path or sub-paths. Note that generic can be * used to specify the wanted class. How ever, if the object loaded from a file is not of a * wanted class, {@link ClassCastException} will be thrown as usual. * * @param <E> * Wanted type. Use object if it is uncertain what types object will be. * @param path * {@link Path} to look in. * @param fileSufix * Ending of the files (extension). * @return List of deserialized objects. * @throws IOException * If {@link IOException} occurs. * @throws SerializationException * If data can not be deserialized. * */ private <E> List<E> getObjectsByFileTreeWalk(Path path, final String fileSufix) throws IOException, SerializationException { if (!Files.isDirectory(path)) { return Collections.emptyList(); } final ISerializer serializer = getSerializationManagerProvider().createSerializer(); final MutableObject mutableException = new MutableObject(); final List<E> returnList = new ArrayList<>(); Files.walkFileTree(path, new SimpleFileVisitor<Path>() { @SuppressWarnings("unchecked") @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (file.toString().endsWith(fileSufix)) { InputStream inputStream = null; Input input = null; try { inputStream = Files.newInputStream(file, StandardOpenOption.READ); input = new Input(inputStream); Object deserialized = serializer.deserialize(input); returnList.add((E) deserialized); } catch (SerializationException e) { mutableException.setValue(e); return FileVisitResult.TERMINATE; } finally { if (null != input) { input.close(); } } } return FileVisitResult.CONTINUE; } }); SerializationException serializationException = (SerializationException) mutableException.getValue(); if (null != serializationException) { throw serializationException; } else { return returnList; } } /** * Returns map of online available storages with their {@link CmrRepositoryDefinition} as a * value. * * @return Map of online available storages with their {@link CmrRepositoryDefinition} as a * value. */ private Map<StorageData, CmrRepositoryDefinition> getOnlineStorages() { Map<StorageData, CmrRepositoryDefinition> storageMap = new HashMap<>(); List<CmrRepositoryDefinition> allRepositories = InspectIT.getDefault().getCmrRepositoryManager().getCmrRepositoryDefinitions(); for (CmrRepositoryDefinition cmrRepositoryDefinition : allRepositories) { if (cmrRepositoryDefinition.getOnlineStatus() == OnlineStatus.ONLINE) { List<StorageData> closedStorages = cmrRepositoryDefinition.getStorageService().getReadableStorages(); if (null != closedStorages) { for (StorageData storageData : closedStorages) { storageMap.put(storageData, cmrRepositoryDefinition); } } } } return storageMap; } /** * {@inheritDoc} */ @Override protected Path getDefaultStorageDirPath() { return Paths.get(Platform.getLocation().toString(), getStorageDefaultFolder()).toAbsolutePath(); } /** * * @return Returns the system username. */ private String getSystemUsername() { return System.getProperty("user.name"); } /** * @param dataRetriever * the httpDataRetriever to set */ public void setDataRetriever(DataRetriever dataRetriever) { this.dataRetriever = dataRetriever; } /** * Sets {@link #dataUploader}. * * @param dataUploader * New value for {@link #dataUploader} */ public void setDataUploader(DataUploader dataUploader) { this.dataUploader = dataUploader; } /** * Sets {@link #storageRepositoryDefinitionProvider}. * * @param storageRepositoryDefinitionProvider * New value for {@link #storageRepositoryDefinitionProvider} */ public void setStorageRepositoryDefinitionProvider(StorageRepositoryDefinitionProvider storageRepositoryDefinitionProvider) { this.storageRepositoryDefinitionProvider = storageRepositoryDefinitionProvider; } }