| /** |
| ******************************************************************************** |
| * Copyright (c) 2019-2021 Robert Bosch GmbH and others. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Robert Bosch GmbH - initial API and implementation |
| ******************************************************************************** |
| */ |
| package org.eclipse.app4mc.amalthea.converters.headless.app; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.text.MessageFormat; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| |
| import org.apache.felix.service.command.Descriptor; |
| import org.apache.felix.service.command.Parameter; |
| import org.eclipse.app4mc.amalthea.converters.common.MigrationException; |
| import org.eclipse.app4mc.amalthea.converters.common.MigrationHelper; |
| import org.eclipse.app4mc.amalthea.converters.common.MigrationInputFile; |
| import org.eclipse.app4mc.amalthea.converters.common.MigrationProcessor; |
| import org.eclipse.app4mc.amalthea.converters.common.MigrationSettings; |
| import org.eclipse.app4mc.amalthea.converters.common.MigrationStatusCode; |
| import org.eclipse.app4mc.amalthea.converters.common.utils.ModelVersion; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.Reference; |
| |
| /** |
| * Command to execute a AMALTHEA model migration. Is registered as Felix Gogo Shell command. |
| */ |
| // suppress logger vulnerability because we explicitly want to write to the console for the executable jar use case |
| @SuppressWarnings(value = { "java:S1148", "java:S106" }) |
| @Component( |
| property = { |
| "osgi.command.scope:String=app4mc", |
| "osgi.command.function:String=convert" |
| }, |
| service = ModelMigrationCommand.class) |
| public class ModelMigrationCommand { |
| |
| @Reference |
| MigrationProcessor migrationProcessor; |
| |
| @Descriptor("Start an APP4MC AMALTHEA model migration") |
| public void convert( |
| @Descriptor("The model version to which the model should be migrated to") |
| @Parameter(absentValue = "latest", names = { "-v", "--version" }) |
| String modelVersion, |
| @Descriptor("true/false whether the migration should be performed recursive on the provided folder or not (default=false)") |
| @Parameter(absentValue = "false", names = { "-r", "--recursive" } ) |
| boolean recursive, |
| @Descriptor("true/false whether the backup files per model file should be created or not (default=false)") |
| @Parameter(absentValue = "false", names = { "-nb", "--nobackup" } ) |
| boolean noBackup, |
| @Descriptor("The filename of the model file or the folder that contains model files to migrate") |
| String filename) { |
| |
| Path modelFilePath = Paths.get(filename).toAbsolutePath(); |
| if (!Files.exists(modelFilePath)) { |
| System.err.println("Model file or folder \""+ filename + "\" does not exist!"); |
| return; |
| } |
| |
| // build up MigrationSettings |
| // same as AmaltheaModelMigrationHandler#collectInput |
| |
| String outputModelVersion = ModelVersion.getLatestVersion(); |
| if (!"latest".equals(modelVersion)) { |
| // verify the input |
| ModelVersion version = ModelVersion.getModelVersion(modelVersion); |
| if (version != null) { |
| outputModelVersion = modelVersion; |
| } else { |
| System.err.println("Migration model version " + modelVersion + " is invalid"); |
| return; |
| } |
| } |
| |
| if (Files.isDirectory(modelFilePath)) { |
| convertDirectory(modelFilePath, outputModelVersion, recursive, noBackup); |
| } else if (modelFilePath.toString().toLowerCase().endsWith(".amxmi")) { |
| // single file migration |
| |
| // build up MigrationSettings |
| // same as AmaltheaModelMigrationHandler#collectInput |
| try (MigrationSettings migrationSettings = new MigrationSettings()) { |
| migrationSettings.setProject(modelFilePath.getParent().toFile()); |
| migrationSettings.setMigrationModelVersion(outputModelVersion); |
| |
| convert(Arrays.asList(modelFilePath.toFile()), migrationSettings, noBackup); |
| } |
| } else { |
| System.err.println("Given parameter \"" + filename + "\" is neither a directory nor a model file!"); |
| } |
| } |
| |
| private void convertDirectory(Path modelFilePath, String outputModelVersion, boolean recursive, boolean noBackup) { |
| try (Stream<Path> directoryStream = Files.walk(modelFilePath, 1)) { |
| List<File> modelFiles = directoryStream |
| .filter(Files::isRegularFile) |
| .filter(file -> file.toString().toLowerCase().endsWith(".amxmi")) |
| .map(Path::toFile) |
| .collect(Collectors.toList()); |
| |
| if (!modelFiles.isEmpty()) { |
| try (MigrationSettings migrationSettings = new MigrationSettings()) { |
| migrationSettings.setProject(modelFilePath.toFile()); |
| migrationSettings.setMigrationModelVersion(outputModelVersion); |
| |
| convert(modelFiles, migrationSettings, noBackup); |
| } |
| } |
| } catch (IOException e) { |
| System.err.println("Failed to load model files"); |
| e.printStackTrace(); |
| return; |
| } |
| |
| if (recursive) { |
| // check for directories and process each directory as separate migration |
| try (Stream<Path> directoryStream = Files.walk(modelFilePath, 1)) { |
| List<Path> modelDirectories = directoryStream |
| .filter(Files::isDirectory) |
| .filter(file -> { |
| try { |
| return !Files.isSameFile(file, modelFilePath); |
| } catch (IOException e) { |
| return false; |
| } |
| }) |
| .collect(Collectors.toList()); |
| |
| for (Path path : modelDirectories) { |
| convertDirectory(path, outputModelVersion, recursive, noBackup); |
| } |
| } catch (IOException e) { |
| System.err.println("Failed to load model files"); |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| private void convert(List<File> inputFiles, MigrationSettings migrationSettings, boolean noBackup) { |
| |
| // same as ModelLoaderJob |
| try { |
| List<MigrationInputFile> modelFiles = MigrationHelper.populateModels(inputFiles, migrationSettings); |
| migrationSettings.getMigModelFiles().addAll(modelFiles); |
| } catch (Exception e) { |
| System.err.println("Failed to load model files"); |
| e.printStackTrace(); |
| return; |
| } |
| |
| // same as AmaltheaModelMigrationHandler#JobChangeListener |
| try { |
| boolean inputValid = MigrationHelper.isInputModelVersionValid(migrationSettings); |
| if (!inputValid) { |
| System.err.println("Model migration stopped in " + migrationSettings.getOutputDirectoryLocation() + " as selected model files belong to different versions"); |
| } else { |
| |
| if (migrationSettings.getInputModelVersion() != null |
| && ModelVersion.getLatestVersion().equals(migrationSettings.getInputModelVersion())) { |
| |
| System.err.println("Selected models are compatible to latest AMALTHEA meta-model version " |
| + ModelVersion.getLatestVersion() |
| + ".\nIt is not required to migrate the models in " |
| + migrationSettings.getOutputDirectoryLocation()); |
| } else { |
| // check if a migration needs to be executed |
| Map<String, String> migStepEntries = MigrationHelper.generateMigrationSteps( |
| migrationSettings.getInputModelVersion(), |
| migrationSettings.getMigrationModelVersion()); |
| |
| if (migStepEntries.size() == 0) { |
| System.err.println("Migration not supported for the selected model versions.\nInput Model version : \"" |
| + migrationSettings.getInputModelVersion() |
| + "\" Output Model Version : \"" |
| + migrationSettings.getMigrationModelVersion() |
| + "\""); |
| return; |
| } |
| |
| // set the file parent folder as output location to convert the file at source |
| MigrationInputFile migrationInputFile = migrationSettings.getMigModelFiles().get(0); |
| migrationSettings.setOutputDirectoryLocation(migrationInputFile.getOriginalFile().getParent()); |
| |
| // Rename or copy the original files to filename_currentversion.amxmi |
| boolean backupSucceeded = true; |
| if (!noBackup) { |
| for (MigrationInputFile input : migrationSettings.getMigModelFiles()) { |
| backupSucceeded = MigrationHelper.createBackupFile(input); |
| } |
| } |
| |
| if (backupSucceeded) { |
| //now call migration job to migrate the file to latest Amalthea version |
| |
| // same as ModelMigrationJob |
| int result = migrationProcessor.execute(migrationSettings, null); |
| switch (result) { |
| |
| case MigrationStatusCode.UNSUPPORTED_MODEL_VERSIONS: |
| System.err.println(MessageFormat.format("Migration in {0} not supported for the selected model versions. \nInput Model version : \"{1}\" Output Model Version : \"{2}\"", |
| migrationSettings.getOutputDirectoryLocation(), |
| migrationSettings.getInputModelVersion(), |
| migrationSettings.getMigrationModelVersion())); |
| break; |
| case MigrationStatusCode.ERROR: |
| System.err.println("Error during migration in " + migrationSettings.getOutputDirectoryLocation()); |
| break; |
| default: |
| System.out.println("Model Migration in " + migrationSettings.getOutputDirectoryLocation() + " successful !!"); |
| } |
| } else { |
| // do nothing as we could not backup source and this can data loss to user if he |
| // does not intend to loose his original model file |
| System.err.println("Migration Stopped : Source files could not be backed up before migration in " |
| + migrationSettings.getOutputDirectoryLocation()); |
| } |
| } |
| } |
| } catch (MigrationException e) { |
| System.err.println("Error during migration in " + migrationSettings.getOutputDirectoryLocation() + " : " + e.getLocalizedMessage()); |
| } |
| |
| } |
| } |