diff --git a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/artifacts/artifacts.module.ts b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/artifacts/artifacts.module.ts index 5040c376cf..7c2bb83007 100644 --- a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/artifacts/artifacts.module.ts +++ b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/artifacts/artifacts.module.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020 Contributors to the Eclipse Foundation + * Copyright (c) 2020-2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -24,7 +24,7 @@ import { WineryModalModule } from '../../../wineryModalModule/winery.modal.modul import { WineryNamespaceSelectorModule } from '../../../wineryNamespaceSelector/wineryNamespaceSelector.module'; import { WineryDuplicateValidatorModule } from '../../../wineryValidators/wineryDuplicateValidator.module'; import { WineryPipesModule } from '../../../wineryPipes/wineryPipes.module'; -import { WineryAddModule } from '../../../wineryAddComponentModule/addComponent.moudle'; +import { WineryAddModule } from '../../../wineryAddComponentModule/addComponent.module'; import { ArtifactsComponent } from './artifacts.component'; import { WineryQNameSelectorModule } from '../../../wineryQNameSelector/wineryQNameSelector.module'; import { ArtifactsService } from './artifacts.service'; diff --git a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/implementations/implementations.module.ts b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/implementations/implementations.module.ts index 17b5829de0..4c02e67449 100644 --- a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/implementations/implementations.module.ts +++ b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/implementations/implementations.module.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Contributors to the Eclipse Foundation + * Copyright (c) 2017-2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -26,7 +26,7 @@ import { WineryModalModule } from '../../../wineryModalModule/winery.modal.modul import { WineryTableModule } from '../../../wineryTableModule/wineryTable.module'; import { ImplementationsComponent } from './implementations.component'; import { WineryPipesModule } from '../../../wineryPipes/wineryPipes.module'; -import { WineryAddModule } from '../../../wineryAddComponentModule/addComponent.moudle'; +import { WineryAddModule } from '../../../wineryAddComponentModule/addComponent.module'; @NgModule({ imports: [ diff --git a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/inheritance/inheritance.module.ts b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/inheritance/inheritance.module.ts index 808c0dd18e..583b5795c6 100644 --- a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/inheritance/inheritance.module.ts +++ b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/inheritance/inheritance.module.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Contributors to the Eclipse Foundation + * Copyright (c) 2017-2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -22,7 +22,7 @@ import { WineryQNameSelectorModule } from '../../../wineryQNameSelector/wineryQN import { SelectModule } from 'ng2-select'; import { BrowserModule } from '@angular/platform-browser'; import { RouterModule } from '@angular/router'; -import { WineryAddModule } from '../../../wineryAddComponentModule/addComponent.moudle'; +import { WineryAddModule } from '../../../wineryAddComponentModule/addComponent.module'; @NgModule({ imports: [ diff --git a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/interfaces/generateArtifactApiData.ts b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/interfaces/generateArtifactApiData.ts index 5805ff9ca0..866485a675 100644 --- a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/interfaces/generateArtifactApiData.ts +++ b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/interfaces/generateArtifactApiData.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Contributors to the Eclipse Foundation + * Copyright (c) 2017-2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -20,9 +20,9 @@ export class GenerateArtifactApiData { autoCreateArtifactTemplate: string; autoGenerateIA: string; // MUST be set - artifactType = '{http://opentosca.org/artifacttypes}WAR'; + // = '{http://opentosca.org/artifacttypes}WAR'; + artifactType: string; artifactSpecificContent: string; interfaceName: string; operationName: string; - javaPackage: string; } diff --git a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/interfaces/interfaces.component.html b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/interfaces/interfaces.component.html index 3f35ba5cc3..8c8b9706a8 100644 --- a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/interfaces/interfaces.component.html +++ b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/interfaces/interfaces.component.html @@ -1,5 +1,5 @@ +
+ +
+
+ + +
+
+ + +
+
+ +
- +
Inputs - * - */ @Component({ selector: 'winery-component-exists', templateUrl: './wineryComponentExists.component.html', }) -export class WineryComponentExistsComponent implements OnInit { +export class WineryComponentExistsComponent implements OnInit, OnChanges { + /** + * This component is for checking whether a given component already exists in the repository and displays it + * accordingly. + * + * + */ @Input() generateData: GenerateData; @Input() modalRef: ModalDirective; + @Input() artifactTypes: ArtifactTypeSelectData[]; + artifactTemplate: String = ToscaTypes.ArtifactTemplate; + selectedArtifactType: ArtifactTypeSelectData; + implementAllOperations = new InterfaceOperationApiData('all'); randomIdSuffix = Math.random(); @@ -52,6 +60,26 @@ export class WineryComponentExistsComponent implements OnInit { if (this.modalRef) { this.modalRef.onShow.subscribe(() => this.checkImplementationExists()); } + if (this.generateData.toscaType === ToscaTypes.ArtifactTemplate) { + for (const value of this.artifactTypes) { + value.text = value.name; + } + this.implementAllOperations = new InterfaceOperationApiData(this.getInterfaceNameWithoutNS()); + if (!this.generateData.selectedOperation) { + this.generateData.selectedOperation = this.implementAllOperations; + } + if (this.generateData.selectedInterface.operations.length === 1) { + this.generateData.selectedOperation = this.generateData.selectedInterface.operations[0]; + } + this.selectedArtifactType = this.artifactTypes[0]; + this.generateData.artifactTypeQName = this.selectedArtifactType.qName; + } + } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.generateData && this.generateData.toscaType === ToscaTypes.ArtifactTemplate) { + this.implementAllOperations = new InterfaceOperationApiData(this.getInterfaceNameWithoutNS()); + } } checkImplementationExists(): void { @@ -76,6 +104,27 @@ export class WineryComponentExistsComponent implements OnInit { ); } } + + typeSelected(type) { + this.generateData.artifactTypeQName = this.artifactTypes.find(value => value.name === type.id).qName; + } + + handleRadioButton(operation: InterfaceOperationApiData) { + const searchValue = RegExp(this.generateData.selectedOperation.name + '-IA$'); + if (operation.name === this.implementAllOperations.name) { + const interfaceNameWithoutNS: string = this.getInterfaceNameWithoutNS(); + this.generateData.name = this.generateData.name.replace(searchValue, interfaceNameWithoutNS + '-IA'); + this.generateData.selectedOperation = operation; + return; + } + this.generateData.name = this.generateData.name.replace(searchValue, operation.name + '-IA'); + this.generateData.selectedOperation = operation; + } + + private getInterfaceNameWithoutNS() { + return this.generateData.selectedInterface.name + .substr(this.generateData.selectedInterface.name.lastIndexOf('/') + 1); + } } export class GenerateData { @@ -85,4 +134,7 @@ export class GenerateData { name: string; url: string; version: WineryVersion; + artifactTypeQName: string; + selectedInterface: InterfacesApiData; + selectedOperation: InterfaceOperationApiData; } diff --git a/org.eclipse.winery.frontends/app/tosca-management/src/app/wineryComponentExists/wineryComponentExists.module.ts b/org.eclipse.winery.frontends/app/tosca-management/src/app/wineryComponentExists/wineryComponentExists.module.ts index 8562ab8475..202edca320 100644 --- a/org.eclipse.winery.frontends/app/tosca-management/src/app/wineryComponentExists/wineryComponentExists.module.ts +++ b/org.eclipse.winery.frontends/app/tosca-management/src/app/wineryComponentExists/wineryComponentExists.module.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Contributors to the Eclipse Foundation + * Copyright (c) 2017-2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -11,15 +11,16 @@ * * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 *******************************************************************************/ -import {NgModule} from '@angular/core'; +import { NgModule } from '@angular/core'; -import {WineryComponentExistsComponent} from './wineryComponentExists.component'; -import {CommonModule} from '@angular/common'; -import {FormsModule} from '@angular/forms'; -import {BrowserModule} from '@angular/platform-browser'; -import {ExistService} from '../wineryUtils/existService'; -import {WineryNamespaceSelectorModule} from '../wineryNamespaceSelector/wineryNamespaceSelector.module'; -import {WineryPipesModule} from '../wineryPipes/wineryPipes.module'; +import { WineryComponentExistsComponent } from './wineryComponentExists.component'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; +import { ExistService } from '../wineryUtils/existService'; +import { WineryNamespaceSelectorModule } from '../wineryNamespaceSelector/wineryNamespaceSelector.module'; +import { WineryPipesModule } from '../wineryPipes/wineryPipes.module'; +import { SelectModule } from 'ng2-select'; @NgModule({ imports: [ @@ -28,6 +29,7 @@ import {WineryPipesModule} from '../wineryPipes/wineryPipes.module'; CommonModule, WineryNamespaceSelectorModule, WineryPipesModule, + SelectModule, ], exports: [WineryComponentExistsComponent], declarations: [WineryComponentExistsComponent], diff --git a/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/BashGenerator.java b/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/BashGenerator.java new file mode 100644 index 0000000000..4d4d35af7e --- /dev/null +++ b/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/BashGenerator.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + *******************************************************************************/ + +package org.eclipse.winery.generators.ia; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.stream.Collectors; + +import org.eclipse.winery.model.tosca.TInterface; +import org.eclipse.winery.model.tosca.TOperation; +import org.eclipse.winery.model.tosca.TParameter; + +public class BashGenerator extends Generator { + + public BashGenerator(TInterface tInterface, String operationName, Path workingDir) { + super(tInterface, operationName, workingDir); + } + + @Override + public void generateImplementationArtifact() throws IOException { + if (operationName.equals("interface")) { + throw new IllegalArgumentException("The whole interface cannot be generated for bash as artifact type!"); + } else { + StringBuilder sb = new StringBuilder(); + TOperation operationToImplement = tInterface.getOperations().stream().parallel().filter(operation1 -> operation1.getName().equals(operationName)).findAny().orElseThrow(IllegalArgumentException::new); + String content = generateBashFileContent(sb, tInterface.getName(), operationToImplement); + Files.write(Paths.get(this.workingDirectory.toString(), operationName + ".sh"), content.getBytes()); + } + } + + /** + * Generates the bash file for the implementation of the provided operation of the provided interface. + * + * @return The contents of the bash file + */ + private String generateBashFileContent(StringBuilder sb, String interfaceName, TOperation operation) { + sb.append("#!/bin/bash\n"); + sb.append("# Implements the ").append(operation.getName()).append(" operation of the ").append(interfaceName).append(" interface\n"); + // Add example call: + sb.append("# Example call: "); + if (operation.getInputParameters() != null) { + operation.getInputParameters().forEach(tParameter -> sb.append(tParameter.getName()).append("=test").append(tParameter.getName()).append(" ")); + } + sb.append("./").append(operationName).append(".sh\n\n"); + + // Add input parameters and assign them to the positional arguments + if (operation.getInputParameters() != null) { + sb.append("# Print input parameters:\n"); + operation.getInputParameters().forEach(tParameter -> sb.append("echo $").append(tParameter.getName()).append(" # Type: ").append(tParameter.getName().replace("xsd:", "")).append("\n")); + } + // Add output parameters + if (operation.getOutputParameters() != null) { + sb.append("\n# The Output parameters and their expected types:\n"); + String outputParametersAndType = operation.getOutputParameters().stream().map(tParameter -> tParameter.getName() + " # Type: " + tParameter.getType().replace("xsd:", "")).collect(Collectors.joining(",\n")); + sb.append(outputParametersAndType); + sb.append("\n"); + } + + sb.append("\n# Your Code here\n\n"); + + if (operation.getOutputParameters() != null) { + sb.append("# Return\n"); + for (TParameter output : operation.getOutputParameters()) { + sb.append("echo \"").append(output.getName()).append("=${").append(output.getName()).append("}\"\n"); + } + } + return sb.toString(); + } +} diff --git a/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/DefaultGenerator.java b/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/DefaultGenerator.java new file mode 100644 index 0000000000..ff961d2d7a --- /dev/null +++ b/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/DefaultGenerator.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + *******************************************************************************/ + +package org.eclipse.winery.generators.ia; + +import java.nio.file.Path; + +import org.eclipse.winery.model.tosca.TInterface; + +/** + * Used if no generator for the given artifact type exists. + * Does not create any files. + */ +public class DefaultGenerator extends Generator { + + public DefaultGenerator(TInterface tInterface, String operationName, Path workingDir) { + super(tInterface, operationName, workingDir); + } + + @Override + public void generateImplementationArtifact() { + + } +} diff --git a/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/Generator.java b/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/Generator.java index 95061522a4..8bb32b1242 100644 --- a/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/Generator.java +++ b/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/Generator.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013-2017 Contributors to the Eclipse Foundation + * Copyright (c) 2022-2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -11,368 +11,67 @@ * * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 *******************************************************************************/ + package org.eclipse.winery.generators.ia; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; import java.net.URL; -import java.nio.charset.Charset; import java.nio.file.Files; -import java.nio.file.OpenOption; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.nio.file.StandardOpenOption; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; +import java.nio.file.Paths; import org.eclipse.winery.model.tosca.TInterface; -import org.eclipse.winery.model.tosca.TOperation; -import org.eclipse.winery.model.tosca.TParameter; -import org.apache.commons.compress.archivers.ArchiveEntry; -import org.apache.commons.compress.archivers.ArchiveOutputStream; -import org.apache.commons.compress.archivers.ArchiveStreamFactory; -import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; -import org.apache.commons.compress.utils.IOUtils; -import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class Generator { +/** + * Used if no generator for the given artifact type exists. + * Does not create any files. + */ +public abstract class Generator { private static final Logger LOGGER = LoggerFactory.getLogger(Generator.class); - - // Placeholder applicable for all files - private static final String PLACEHOLDER_JAVA_PACKAGE = "IA_PACKAGE"; - private static final String PLACEHOLDER_NAMESPACE = "IA_NAMESPACE"; - private static final String PLACEHOLDER_CLASS_NAME = "IA_CLASS_NAME"; - private static final String PLACEHOLDER_IA_ARTIFACT_TEMPLATE_UPLOAD_URL = "IA_ARTIFACT_TEMPLATE_UPLOAD_URL"; - - // Placeholders in Java Service Files - private static final String PLACEHOLDER_GENERATED_WEBSERVICE_METHODS = "GENERATED_WEBSERVICE_METHODS"; - - // Template folder relative to resources folder in this project - private static final String TEMPLATE_PROJECT_FOLDER = "template/project"; - private static final String TEMPLATE_JAVA_FOLDER = "template/java"; - - private static final String TEMPLATE_JAVA_ABSTRACT_IA_SERVICE = "AbstractIAService.java.template"; - private static final String TEMPLATE_JAVA_TEMPLATE_SERVICE = "TemplateService.java.template"; - - private final TInterface tInterface; - private final File workingDir; - private final File outDir; - private final String name; - private final String javaPackage; - private final String namespace; - private final URL iaArtifactTemplateUploadUrl; - - /** - * Creates a new IA Generator instance for the given {@link TInterface}. - * - * @param tInterface TOSCA interface to generate the IA for - * @param packageAndNamespace Package to be used for the generated Java code, e.g. 'org.opentosca.ia'. To - * generate the respective Namespace for the Web Service the components of the - * package are reverted, prepended with 'http://' and appended with '/'. This is - * provided by the user in a textfield in the Winery UI. - * @param iaArtifactTemplateUploadUrl The URL to which the generated IA should be posted. - * @param name unique and valid name to be used for the generated maven project name, java - * project name, class name, port type name. - * @param workingDir working directory to generate the files. This directory also will contain the - * ZIP file with the Eclipse project after generating it. - */ - public Generator(TInterface tInterface, String packageAndNamespace, URL iaArtifactTemplateUploadUrl, String name, File workingDir) { - super(); + + // The name of the operation that an ImplementationArtifact should be generated for, if the name is "interface" an + // ImplementationArtifact for each operation will be generated + final String operationName; + final TInterface tInterface; + final Path workingDirectory; + + public Generator(TInterface tInterface, String operationName, Path workingDir) { + this.workingDirectory = workingDir; + this.operationName = operationName; this.tInterface = tInterface; - this.javaPackage = packageAndNamespace; - this.iaArtifactTemplateUploadUrl = iaArtifactTemplateUploadUrl; - this.name = name; - this.workingDir = new File(workingDir.getAbsolutePath() + File.separator + this.name); - this.outDir = new File(workingDir.getAbsolutePath()); - - if (this.workingDir.exists()) { - Generator.LOGGER.error("Workdir " + this.workingDir + " already exits. This might lead to corrupted results if it is not empty!"); - } - - // Generate Namespace - String[] splitPkg = this.javaPackage.split("\\."); - String tmpNamespace = "http://"; - for (int i = splitPkg.length - 1; i >= 0; i--) { - tmpNamespace += splitPkg[i]; - // Add '.' if it is not the last iterations - if (i != 0) { - tmpNamespace += "."; - } - } - this.namespace = tmpNamespace += "/"; - } - - /** - * Generates the project and puts the files into a subdirectory of working dir - * - * @return the folder where the generated files are contained in - */ - public Path generateProject() throws Exception { - Path workingDirPath = this.workingDir.toPath(); - Files.createDirectories(workingDirPath); - - // directory to store the template files to generate the java files from - Path javaTemplateDir = workingDirPath.resolve("../java"); - Files.createDirectories(javaTemplateDir); - - // Copy template project and template java files - String s = this.getClass().getResource("").getPath(); - if (s.contains("jar!")) { - Generator.LOGGER.trace("we work on a jar file"); - Generator.LOGGER.trace("Location of the current class: {}", s); - - // we have a jar file - // format: file:/location...jar!...path-in-the-jar - // we only want to have location :) - int excl = s.lastIndexOf("!"); - s = s.substring(0, excl); - s = s.substring("file:".length()); - - try (JarFile jf = new JarFile(s);) { - Enumeration entries = jf.entries(); - while (entries.hasMoreElements()) { - JarEntry je = entries.nextElement(); - String name = je.getName(); - if (name.startsWith(Generator.TEMPLATE_PROJECT_FOLDER + "/") && (name.length() > (Generator.TEMPLATE_PROJECT_FOLDER.length() + 1))) { - // strip "template/" from the beginning to have paths without "template" starting relatively from the working dir - name = name.substring(Generator.TEMPLATE_PROJECT_FOLDER.length() + 1); - if (je.isDirectory()) { - // directory found - Path dir = workingDirPath.resolve(name); - Files.createDirectory(dir); - } else { - Path file = workingDirPath.resolve(name); - try (InputStream is = jf.getInputStream(je);) { - Files.copy(is, file, StandardCopyOption.REPLACE_EXISTING); - } - } - } else if (name.startsWith(Generator.TEMPLATE_JAVA_FOLDER + "/") && (name.length() > (Generator.TEMPLATE_JAVA_FOLDER.length() + 1))) { - if (!je.isDirectory()) { - // we copy the file directly into javaTemplateDir - File f = new File(name); - Path file = javaTemplateDir.resolve(f.getName()); - try (InputStream is = jf.getInputStream(je);) { - Files.copy(is, file, StandardCopyOption.REPLACE_EXISTING); - } - } - } - } - } - } else { - // we're running in debug mode, we can work on the plain file system - File templateProjectDir = new File(this.getClass().getResource("/" + Generator.TEMPLATE_PROJECT_FOLDER).getFile()); - FileUtils.copyDirectory(templateProjectDir, this.workingDir); - - File javaTemplatesDir = new File(this.getClass().getResource("/" + Generator.TEMPLATE_JAVA_FOLDER).getFile()); - FileUtils.copyDirectory(javaTemplatesDir, javaTemplateDir.toFile()); - } - - // Create Java Code Folder - String[] splitPkg = this.javaPackage.split("\\."); - StringBuilder javaFolderString = new StringBuilder(this.workingDir.getAbsolutePath() + File.separator + "src" + File.separator + "main" + File.separator + "java"); - for (String value : splitPkg) { - javaFolderString.append(File.separator).append(value); + + if (Files.exists(this.workingDirectory)) { + LOGGER.error("Workdir " + this.workingDirectory + " already exits. This might lead to corrupted results if it is not empty!"); } - - // Copy and rename TEMPLATE_JAVA_TEMPLATE_SERVICE - Path templateJavaService = javaTemplateDir.resolve(Generator.TEMPLATE_JAVA_TEMPLATE_SERVICE); - File javaService = new File(javaFolderString + File.separator + this.name + ".java"); - Files.createDirectories(javaService.toPath().getParent()); - Files.copy(templateJavaService, javaService.toPath(), StandardCopyOption.REPLACE_EXISTING); - - this.generateJavaFile(javaService); - this.updateFilesRecursively(this.workingDir); - - return this.workingDir.toPath(); - } - - /** - * Generates the IA project into the working dir and zipps it - * - * @return The ZIP file containing the maven/eclipse project to be downloaded by the user. - */ - public File generateAndZipProject() throws Exception { - final Path path = this.generateProject(); - return this.packageProject(path); } - private void generateJavaFile(File javaService) throws IOException { - - // Generate methods - StringBuilder sb = new StringBuilder(); - - for (TOperation op : this.tInterface.getOperations()) { - // Annotations - sb.append("\t@WebMethod\n"); - sb.append("\t@SOAPBinding\n"); - sb.append("\t@Oneway\n"); - - // Signatur - String operationReturn = "void"; - sb.append("\tpublic ").append(operationReturn).append(" ").append(op.getName()).append("(\n"); - - // Parameter - boolean first = true; - if (op.getInputParameters() != null) { - for (TParameter parameter : op.getInputParameters()) { - String parameterName = parameter.getName(); - - if (first) { - first = false; - sb.append("\t\t"); - } else { - sb.append(",\n\t\t"); - } - - // Generate @WebParam - sb.append("@WebParam(name=\"").append(parameterName).append("\", targetNamespace=\"").append(this.namespace).append("\") "); - - sb.append("String ").append(parameterName); - } - } - sb.append("\n\t) {\n"); - - // If there are output parameters we generate the respective HashMap - boolean outputParamsExist = (op.getOutputParameters() != null) && (!op.getOutputParameters().isEmpty()); - if (outputParamsExist) { - sb.append("\t\t// This HashMap holds the return parameters of this operation.\n"); - sb.append("\t\tfinal HashMap returnParameters = new HashMap();\n\n"); - } - - sb.append("\t\t// TODO: Implement your operation here.\n"); - - // Generate code to set output parameters - if (outputParamsExist) { - for (TParameter outputParam : op.getOutputParameters()) { - sb.append("\n\n\t\t// Output Parameter '").append(outputParam.getName()).append("' "); - if (outputParam.getRequired()) { - sb.append("(required)"); - } else { - sb.append("(optional)"); - } - sb.append("\n\t\t// TODO: Set ").append(outputParam.getName()).append(" parameter here."); - sb.append("\n\t\t// Do NOT delete the next line of code. Set \"\" as value if you want to return nothing or an empty result!"); - sb.append("\n\t\treturnParameters.put(\"").append(outputParam.getName()).append("\", \"TODO\");"); - } - sb.append("\n\n\t\tsendResponse(returnParameters);\n"); - } - - sb.append("\t}\n\n"); - } - - // Read file and replace placeholders - Charset cs = Charset.defaultCharset(); - List lines = new ArrayList<>(); - for (String line : Files.readAllLines(javaService.toPath(), cs)) { - // Replace web service method - line = line.replaceAll(Generator.PLACEHOLDER_GENERATED_WEBSERVICE_METHODS, sb.toString()); - lines.add(line); - } - - // Write file - OpenOption[] options = new OpenOption[] {StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING}; - Files.write(javaService.toPath(), lines, cs, options); - } - - /** - * Iterates recursively through all the files in the project working directory and tries to replace the global - * placeholders. - * - * @param folderOrFile to start with - */ - private void updateFilesRecursively(File folderOrFile) { - if (folderOrFile.isFile()) { - - if (folderOrFile.getAbsolutePath().endsWith(".jar")) { - return; - } - - Generator.LOGGER.trace("Updating file " + folderOrFile); - - try { - // Read file and replace placeholders - Charset cs = Charset.defaultCharset(); - List lines = new ArrayList<>(); - for (String line : Files.readAllLines(folderOrFile.toPath(), cs)) { - line = line.replaceAll(Generator.PLACEHOLDER_CLASS_NAME, this.name); - line = line.replaceAll(Generator.PLACEHOLDER_JAVA_PACKAGE, this.javaPackage); - line = line.replaceAll(Generator.PLACEHOLDER_NAMESPACE, this.namespace); - line = line.replaceAll(Generator.PLACEHOLDER_IA_ARTIFACT_TEMPLATE_UPLOAD_URL, this.iaArtifactTemplateUploadUrl.toString()); - lines.add(line); - } - - // Write file - OpenOption[] options = new OpenOption[] {StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING}; - Files.write(folderOrFile.toPath(), lines, cs, options); - } catch (IOException e) { - e.printStackTrace(); - } - } else { - Generator.LOGGER.trace("Updating folder " + folderOrFile); - for (File childFile : folderOrFile.listFiles()) { - this.updateFilesRecursively(childFile); - } - } - } - - /** - * Packages the generated project into a ZIP file which is stored in outDir and has the name of the Project. - * - * @param projectDir the dir where the generated files reside in - * @return ZIP file - */ - private File packageProject(Path projectDir) { - try { - File packagedProject = new File(this.outDir.getAbsoluteFile() + File.separator + this.name + ".zip"); - FileOutputStream fileOutputStream = new FileOutputStream(packagedProject); - final ArchiveOutputStream zos = new ArchiveStreamFactory().createArchiveOutputStream("zip", fileOutputStream); - - this.addFilesRecursively(projectDir.toAbsolutePath().toFile(), projectDir.toAbsolutePath() + File.separator, zos); - - zos.finish(); - zos.close(); - - return packagedProject; - } catch (Exception e) { - e.printStackTrace(); - } - return null; + public Path generateArtifact() throws Exception { + Files.createDirectories(this.workingDirectory); + Files.createFile(Paths.get(this.workingDirectory.toString(), "README.md")); + generateImplementationArtifact(); + return this.workingDirectory; } /** - * Recursive Helper function for packageProject() - * - * @param folderOrFile to add into the archive - * @param baseDir the base directory, which is to be stripped from the file name - * @param zos ArchiveOutputStream to add the files to + * Hook used for the generation of the actual artifacts within the created working directory */ - private void addFilesRecursively(File folderOrFile, String baseDir, ArchiveOutputStream zos) { - if (folderOrFile.isFile()) { - String nameOfFileInZip = folderOrFile.getAbsolutePath().replace(baseDir, ""); - Generator.LOGGER.trace("Adding " + folderOrFile + " as " + nameOfFileInZip); - ArchiveEntry archiveEntry = new ZipArchiveEntry(nameOfFileInZip); - try (InputStream is = new FileInputStream(folderOrFile)) { - zos.putArchiveEntry(archiveEntry); - IOUtils.copy(is, zos); - zos.closeArchiveEntry(); - } catch (Exception e) { - e.printStackTrace(); - } - } else { - Generator.LOGGER.trace("Adding folder " + folderOrFile); - for (File childFile : folderOrFile.listFiles()) { - this.addFilesRecursively(childFile, baseDir, zos); - } + public abstract void generateImplementationArtifact() throws Exception; + + public static Generator getGenerator(String artifactType, TInterface tInterface, String packageAndNamespace, URL iaArtifactTemplateUploadUrl, String name, Path workingDir, String operation) { + artifactType = artifactType.substring(artifactType.lastIndexOf("}") + 1); + switch (artifactType) { + case "Bash": + return new BashGenerator(tInterface, operation, workingDir); + case "Python": + return new PythonGenerator(tInterface, operation, workingDir); + case "WAR": + case "JAR": + return new JarAndWarGenerator(tInterface, packageAndNamespace, iaArtifactTemplateUploadUrl, name, workingDir); + default: + return new DefaultGenerator(tInterface, operation, workingDir); } } } diff --git a/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/JarAndWarGenerator.java b/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/JarAndWarGenerator.java new file mode 100644 index 0000000000..3fec340950 --- /dev/null +++ b/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/JarAndWarGenerator.java @@ -0,0 +1,372 @@ +/******************************************************************************* + * Copyright (c) 2013-2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + *******************************************************************************/ +package org.eclipse.winery.generators.ia; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import org.eclipse.winery.model.tosca.TInterface; +import org.eclipse.winery.model.tosca.TOperation; +import org.eclipse.winery.model.tosca.TParameter; + +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveOutputStream; +import org.apache.commons.compress.archivers.ArchiveStreamFactory; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.utils.IOUtils; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JarAndWarGenerator extends Generator { + + private static final Logger LOGGER = LoggerFactory.getLogger(JarAndWarGenerator.class); + + // Placeholder applicable for all files + private static final String PLACEHOLDER_JAVA_PACKAGE = "IA_PACKAGE"; + private static final String PLACEHOLDER_NAMESPACE = "IA_NAMESPACE"; + private static final String PLACEHOLDER_CLASS_NAME = "IA_CLASS_NAME"; + private static final String PLACEHOLDER_IA_ARTIFACT_TEMPLATE_UPLOAD_URL = "IA_ARTIFACT_TEMPLATE_UPLOAD_URL"; + + // Placeholders in Java Service Files + private static final String PLACEHOLDER_GENERATED_WEBSERVICE_METHODS = "GENERATED_WEBSERVICE_METHODS"; + + // Template folder relative to resources folder in this project + private static final String TEMPLATE_PROJECT_FOLDER = "template/project"; + private static final String TEMPLATE_JAVA_FOLDER = "template/java"; + + private static final String TEMPLATE_JAVA_ABSTRACT_IA_SERVICE = "AbstractIAService.java.template"; + private static final String TEMPLATE_JAVA_TEMPLATE_SERVICE = "TemplateService.java.template"; + + private final TInterface tInterface; + private final File outDir; + private final String name; + private final String javaPackage; + private final String namespace; + private final URL iaArtifactTemplateUploadUrl; + + /** + * Creates a new IA Generator instance for the given {@link TInterface}. + * + * @param tInterface TOSCA interface to generate the IA for + * @param packageAndNamespace Package to be used for the generated Java code, e.g. 'org.opentosca.ia'. To + * generate the respective Namespace for the Web Service the components of the + * package are reverted, prepended with 'http://' and appended with '/'. This is + * provided by the user in a textfield in the Winery UI. + * @param iaArtifactTemplateUploadUrl The URL to which the generated IA should be posted. + * @param name unique and valid name to be used for the generated maven project name, java + * project name, class name, port type name. + * @param workingDir working directory to generate the files. This directory also will contain the + * ZIP file with the Eclipse project after generating it. + */ + public JarAndWarGenerator(TInterface tInterface, String packageAndNamespace, URL iaArtifactTemplateUploadUrl, String name, Path workingDir) { + super(tInterface, "", Paths.get(workingDir.toString(), name)); + this.tInterface = tInterface; + this.javaPackage = packageAndNamespace; + this.name = name; + this.iaArtifactTemplateUploadUrl = iaArtifactTemplateUploadUrl; + this.outDir = workingDirectory.toAbsolutePath().toFile(); + + if (Files.exists(this.workingDirectory)) { + LOGGER.error("Workdir " + this.workingDirectory + " already exits. This might lead to corrupted results if it is not empty!"); + } + + if (javaPackage == null) { + this.namespace = null; + } else { + // Generate Namespace + String[] splitPkg = this.javaPackage.split("\\."); + StringBuilder tmpNamespace = new StringBuilder("http://"); + for (int i = splitPkg.length - 1; i >= 0; i--) { + tmpNamespace.append(splitPkg[i]); + // Add '.' if it is not the last iterations + if (i != 0) { + tmpNamespace.append("."); + } + } + this.namespace = tmpNamespace.append("/").toString(); + } + } + + @Override + public void generateImplementationArtifact() throws IOException { + // directory to store the template files to generate the java files from + Path javaTemplateDir = workingDirectory.resolve("../java"); + Files.createDirectories(javaTemplateDir); + + // Copy template project and template java files + String s = this.getClass().getResource("").getPath(); + if (s.contains("jar!")) { + JarAndWarGenerator.LOGGER.trace("we work on a jar file"); + JarAndWarGenerator.LOGGER.trace("Location of the current class: {}", s); + + // we have a jar file + // format: file:/location...jar!...path-in-the-jar + // we only want to have location :) + int excl = s.lastIndexOf("!"); + s = s.substring(0, excl); + s = s.substring("file:".length()); + + try (JarFile jf = new JarFile(s)) { + Enumeration entries = jf.entries(); + while (entries.hasMoreElements()) { + JarEntry je = entries.nextElement(); + String name = je.getName(); + if (name.startsWith(JarAndWarGenerator.TEMPLATE_PROJECT_FOLDER + "/") && (name.length() > (JarAndWarGenerator.TEMPLATE_PROJECT_FOLDER.length() + 1))) { + // strip "template/" from the beginning to have paths without "template" starting relatively from the working dir + name = name.substring(JarAndWarGenerator.TEMPLATE_PROJECT_FOLDER.length() + 1); + if (je.isDirectory()) { + // directory found + Path dir = workingDirectory.resolve(name); + Files.createDirectory(dir); + } else { + Path file = workingDirectory.resolve(name); + try (InputStream is = jf.getInputStream(je)) { + Files.copy(is, file, StandardCopyOption.REPLACE_EXISTING); + } + } + } else if (name.startsWith(JarAndWarGenerator.TEMPLATE_JAVA_FOLDER + "/") && (name.length() > (JarAndWarGenerator.TEMPLATE_JAVA_FOLDER.length() + 1))) { + if (!je.isDirectory()) { + // we copy the file directly into javaTemplateDir + File f = new File(name); + Path file = javaTemplateDir.resolve(f.getName()); + try (InputStream is = jf.getInputStream(je)) { + Files.copy(is, file, StandardCopyOption.REPLACE_EXISTING); + } + } + } + } + } + } else { + // we're running in debug mode, we can work on the plain file system + File templateProjectDir = new File(this.getClass().getResource("/" + JarAndWarGenerator.TEMPLATE_PROJECT_FOLDER).getFile()); + FileUtils.copyDirectory(templateProjectDir, this.workingDirectory.toFile()); + + File javaTemplatesDir = new File(this.getClass().getResource("/" + JarAndWarGenerator.TEMPLATE_JAVA_FOLDER).getFile()); + FileUtils.copyDirectory(javaTemplatesDir, javaTemplateDir.toFile()); + } + + // Create Java Code Folder + String[] splitPkg = this.javaPackage.split("\\."); + StringBuilder javaFolderString = new StringBuilder(this.workingDirectory.toAbsolutePath() + File.separator + "src" + File.separator + "main" + File.separator + "java"); + for (String value : splitPkg) { + javaFolderString.append(File.separator).append(value); + } + + // Copy and rename TEMPLATE_JAVA_TEMPLATE_SERVICE + Path templateJavaService = javaTemplateDir.resolve(JarAndWarGenerator.TEMPLATE_JAVA_TEMPLATE_SERVICE); + File javaService = new File(javaFolderString + File.separator + this.name + ".java"); + Files.createDirectories(javaService.toPath().getParent()); + Files.copy(templateJavaService, javaService.toPath(), StandardCopyOption.REPLACE_EXISTING); + this.generateJavaFile(javaService); + + this.updateFilesRecursively(this.workingDirectory.toFile()); + } + + /** + * Generates the IA project into the working dir and zipps it + * + * @return The ZIP file containing the maven/eclipse project to be downloaded by the user. + */ + public File generateAndZipProject() throws Exception { + final Path path = this.generateArtifact(); + return this.packageProject(path); + } + + private void generateJavaFile(File javaService) throws IOException { + + // Generate methods + StringBuilder sb = new StringBuilder(); + + for (TOperation op : this.tInterface.getOperations()) { + // Annotations + sb.append("\t@WebMethod\n"); + sb.append("\t@SOAPBinding\n"); + sb.append("\t@Oneway\n"); + + // Signatur + String operationReturn = "void"; + sb.append("\tpublic ").append(operationReturn).append(" ").append(op.getName()).append("(\n"); + + // Parameter + boolean first = true; + if (op.getInputParameters() != null) { + for (TParameter parameter : op.getInputParameters()) { + String parameterName = parameter.getName(); + + if (first) { + first = false; + sb.append("\t\t"); + } else { + sb.append(",\n\t\t"); + } + + // Generate @WebParam + sb.append("@WebParam(name=\"").append(parameterName).append("\", targetNamespace=\"").append(this.namespace).append("\") "); + + sb.append("String ").append(parameterName); + } + } + sb.append("\n\t) {\n"); + + // If there are output parameters we generate the respective HashMap + boolean outputParamsExist = (op.getOutputParameters() != null) && (!op.getOutputParameters().isEmpty()); + if (outputParamsExist) { + sb.append("\t\t// This HashMap holds the return parameters of this operation.\n"); + sb.append("\t\tfinal HashMap returnParameters = new HashMap();\n\n"); + } + + sb.append("\t\t// TODO: Implement your operation here.\n"); + + // Generate code to set output parameters + if (outputParamsExist) { + for (TParameter outputParam : op.getOutputParameters()) { + sb.append("\n\n\t\t// Output Parameter '").append(outputParam.getName()).append("' "); + if (outputParam.getRequired()) { + sb.append("(required)"); + } else { + sb.append("(optional)"); + } + sb.append("\n\t\t// TODO: Set ").append(outputParam.getName()).append(" parameter here."); + sb.append("\n\t\t// Do NOT delete the next line of code. Set \"\" as value if you want to return nothing or an empty result!"); + sb.append("\n\t\treturnParameters.put(\"").append(outputParam.getName()).append("\", \"TODO\");"); + } + sb.append("\n\n\t\tsendResponse(returnParameters);\n"); + } + + sb.append("\t}\n\n"); + } + + // Read file and replace placeholders + Charset cs = Charset.defaultCharset(); + List lines = new ArrayList<>(); + for (String line : Files.readAllLines(javaService.toPath(), cs)) { + // Replace web service method + line = line.replaceAll(JarAndWarGenerator.PLACEHOLDER_GENERATED_WEBSERVICE_METHODS, sb.toString()); + lines.add(line); + } + + // Write file + OpenOption[] options = new OpenOption[] {StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING}; + Files.write(javaService.toPath(), lines, cs, options); + } + + /** + * Iterates recursively through all the files in the project working directory and tries to replace the global + * placeholders. + * + * @param folderOrFile to start with + */ + private void updateFilesRecursively(File folderOrFile) { + if (folderOrFile.isFile()) { + + if (folderOrFile.getAbsolutePath().endsWith(".jar")) { + return; + } + + JarAndWarGenerator.LOGGER.trace("Updating file " + folderOrFile); + + try { + // Read file and replace placeholders + Charset cs = Charset.defaultCharset(); + List lines = new ArrayList<>(); + for (String line : Files.readAllLines(folderOrFile.toPath(), cs)) { + line = line.replaceAll(JarAndWarGenerator.PLACEHOLDER_CLASS_NAME, this.name); + line = line.replaceAll(JarAndWarGenerator.PLACEHOLDER_JAVA_PACKAGE, this.javaPackage); + line = line.replaceAll(JarAndWarGenerator.PLACEHOLDER_NAMESPACE, this.namespace); + line = line.replaceAll(JarAndWarGenerator.PLACEHOLDER_IA_ARTIFACT_TEMPLATE_UPLOAD_URL, this.iaArtifactTemplateUploadUrl.toString()); + lines.add(line); + } + + // Write file + OpenOption[] options = new OpenOption[] {StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING}; + Files.write(folderOrFile.toPath(), lines, cs, options); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + JarAndWarGenerator.LOGGER.trace("Updating folder " + folderOrFile); + for (File childFile : folderOrFile.listFiles()) { + this.updateFilesRecursively(childFile); + } + } + } + + /** + * Packages the generated project into a ZIP file which is stored in outDir and has the name of the Project. + * + * @param projectDir the dir where the generated files reside in + * @return ZIP file + */ + private File packageProject(Path projectDir) { + try { + File packagedProject = new File(this.outDir.getAbsoluteFile() + File.separator + this.name + ".zip"); + FileOutputStream fileOutputStream = new FileOutputStream(packagedProject); + final ArchiveOutputStream zos = new ArchiveStreamFactory().createArchiveOutputStream("zip", fileOutputStream); + + this.addFilesRecursively(projectDir.toAbsolutePath().toFile(), projectDir.toAbsolutePath() + File.separator, zos); + + zos.finish(); + zos.close(); + + return packagedProject; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * Recursive Helper function for packageProject() + * + * @param folderOrFile to add into the archive + * @param baseDir the base directory, which is to be stripped from the file name + * @param zos ArchiveOutputStream to add the files to + */ + private void addFilesRecursively(File folderOrFile, String baseDir, ArchiveOutputStream zos) { + if (folderOrFile.isFile()) { + String nameOfFileInZip = folderOrFile.getAbsolutePath().replace(baseDir, ""); + JarAndWarGenerator.LOGGER.trace("Adding " + folderOrFile + " as " + nameOfFileInZip); + ArchiveEntry archiveEntry = new ZipArchiveEntry(nameOfFileInZip); + try (InputStream is = new FileInputStream(folderOrFile)) { + zos.putArchiveEntry(archiveEntry); + IOUtils.copy(is, zos); + zos.closeArchiveEntry(); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + JarAndWarGenerator.LOGGER.trace("Adding folder " + folderOrFile); + for (File childFile : folderOrFile.listFiles()) { + this.addFilesRecursively(childFile, baseDir, zos); + } + } + } +} diff --git a/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/PythonGenerator.java b/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/PythonGenerator.java new file mode 100644 index 0000000000..6903e7cf9a --- /dev/null +++ b/org.eclipse.winery.generators.ia/src/main/java/org/eclipse/winery/generators/ia/PythonGenerator.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + *******************************************************************************/ + +package org.eclipse.winery.generators.ia; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.stream.Collectors; + +import org.eclipse.winery.model.tosca.TInterface; +import org.eclipse.winery.model.tosca.TOperation; +import org.eclipse.winery.model.tosca.TParameter; + +public class PythonGenerator extends Generator { + // According to https://peps.python.org/pep-0008/#indentation + private static final String indentation = " "; + + public PythonGenerator(TInterface tInterface, String operationName, Path workingDir) { + super(tInterface, operationName, workingDir); + } + + @Override + public void generateImplementationArtifact() throws IOException { + if (operationName.equals("interface")) { + throw new IllegalArgumentException("The whole interface cannot be generated for bash as artifact type!"); + } else { + StringBuilder sb = new StringBuilder(); + TOperation operationToImplement = tInterface.getOperations().stream().parallel().filter(operation1 -> operation1.getName().equals(operationName)).findAny().orElseThrow(IllegalArgumentException::new); + String content = generatePythonFileContent(sb, tInterface.getName(), operationToImplement); + Files.write(Paths.get(this.workingDirectory.toString(), operationName + ".py"), content.getBytes()); + } + } + + private String generatePythonFileContent(StringBuilder sb, String interfaceName, TOperation operation) { + sb.append("#!/usr/bin/env python\n"); + + // Import statements: + if (operation.getInputParameters() != null) { + sb.append("from argparse import ArgumentParser\n"); + } + sb.append("\n"); + sb.append("# Implements the ").append(operation.getName()).append(" operation of the ").append(interfaceName).append(" interface\n"); + // Add example call: + sb.append("# Example call: "); + sb.append("python ").append(operationName).append(".py "); + if (operation.getInputParameters() != null) { + operation.getInputParameters().forEach(tParameter -> sb.append("-").append(tParameter.getName()).append(" test").append(tParameter.getName()).append(" ")); + } + sb.append("\n\n"); + + // define operation as method + String params = ""; + if (operation.getInputParameters() != null) { + params = operation.getInputParameters().stream().map(tParameter -> tParameter.getName() + "=None").collect(Collectors.joining(", ")); + } + sb.append("def ").append(operation.getName()).append("("); + sb.append(params); + sb.append("):\n"); + // Print inputs and provide types as comments + if (operation.getInputParameters() != null) { + sb.append(indentation).append("# Print input parameters:\n"); + operation.getInputParameters().forEach(tParameter -> sb.append(indentation).append("print(").append(tParameter.getName()).append(")").append(" # Type: ").append(tParameter.getType().replace("xsd:", "")).append("\n")); + sb.append("\n"); + } + sb.append(indentation).append("# Your code here\n"); + sb.append("\n"); + + // define main method + sb.append("def main():\n"); + + if (operation.getInputParameters() != null) { + // handle args parsing + sb.append(indentation).append("parser = ArgumentParser()\n"); + operation.getInputParameters().forEach(tParameter -> sb.append(indentation) + .append("parser.add_argument('-") + .append(tParameter.getName()) + .append("', '--") + .append(tParameter.getName()) + .append("', type=") + // Todo: add lookup for the argument type from winery to argparse type (currently default: string) + .append("str") + .append(", dest='") + .append(tParameter.getName()) + .append("')\n") + ); + sb.append(indentation).append("args = parser.parse_args()\n\n"); + + // TODO: Should we check for non-none args if they are required or set the required flag in argparse??? + } + + // call operation method + // Add output parameters + sb.append(indentation); + if (operation.getOutputParameters() != null) { + sb.append(operation.getOutputParameters().stream().map(TParameter::getName).collect(Collectors.joining(", "))); + sb.append(" = "); + } + sb.append(operation.getName()).append("("); + if (operation.getInputParameters() != null) { + sb.append(operation.getInputParameters().stream().map(tParameter -> tParameter.getName() + "=args." + tParameter.getName()).collect(Collectors.joining(", "))); + } + sb.append(")\n"); + + // Print output + if (operation.getOutputParameters() != null) { + sb.append(indentation).append("# Return\n"); + operation.getOutputParameters().forEach(tParameter -> sb.append(indentation).append("print('").append(tParameter.getName()).append("='").append(" + ").append("str(").append(tParameter.getName()).append("))\n")); + } + + sb.append(indentation).append("quit()\n\n"); + + sb.append("if __name__ == \"__main__\":\n").append(indentation).append("main()"); + + return sb.toString(); + } +} diff --git a/org.eclipse.winery.generators.ia/src/test/java/org/eclipse/winery/generators/ia/BashGeneratorTest.java b/org.eclipse.winery.generators.ia/src/test/java/org/eclipse/winery/generators/ia/BashGeneratorTest.java new file mode 100644 index 0000000000..d911f62e2e --- /dev/null +++ b/org.eclipse.winery.generators.ia/src/test/java/org/eclipse/winery/generators/ia/BashGeneratorTest.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2013-2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + *******************************************************************************/ +package org.eclipse.winery.generators.ia; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; + +import org.eclipse.winery.model.tosca.TInterface; +import org.eclipse.winery.model.tosca.TOperation; +import org.eclipse.winery.model.tosca.TParameter; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +public class BashGeneratorTest { + + @TempDir + + private Path wd; + + @Test + public void testMultipleOpsWithInOutParams() { + TOperation install = new TOperation.Builder("install") + .addInputParameter( + new TParameter.Builder("VMIP", "xs:string").build() + ) + .addInputParameter( + new TParameter.Builder("DBMSUsername", "xs:string").build() + ) + .addOutputParameter( + new TParameter.Builder("Output", "xs:string").build() + ) + .build(); + + TOperation uninstall = new TOperation.Builder("uninstall") + .addInputParameter( + new TParameter.Builder("SomeLongParameterName", "xs:string").build() + ) + .addInputParameter( + new TParameter.Builder("Port", "xs:string").build() + ) + .addOutputParameter( + new TParameter.Builder("Output", "xs:string").build() + ) + .build(); + + TInterface iFace = new TInterface.Builder( + "http://www.example.org/interfaces/lifecycle", + Arrays.asList( + install, + uninstall + ) + ).build(); + + BashGenerator gen = new BashGenerator(iFace, + "interface", + wd + ); + Assertions.assertThrows(IllegalArgumentException.class, gen::generateArtifact); + } + + @Test + public void testOneOpNoParams() throws Exception { + TInterface iFace = new TInterface.Builder( + "http://www.example.org/interfaces/lifecycle", + Collections.singletonList( + new TOperation.Builder("install").build() + ) + ).build(); + + BashGenerator gen = new BashGenerator(iFace, + "install", + wd); + gen.generateArtifact(); + Assertions.assertTrue(Files.exists(wd.resolve("README.md"))); + Assertions.assertTrue(Files.exists(wd.resolve("install.sh"))); + } +} diff --git a/org.eclipse.winery.generators.ia/src/test/java/org/eclipse/winery/generators/ia/GeneratorTest.java b/org.eclipse.winery.generators.ia/src/test/java/org/eclipse/winery/generators/ia/JarAndWarGeneratorTest.java similarity index 80% rename from org.eclipse.winery.generators.ia/src/test/java/org/eclipse/winery/generators/ia/GeneratorTest.java rename to org.eclipse.winery.generators.ia/src/test/java/org/eclipse/winery/generators/ia/JarAndWarGeneratorTest.java index 0822441471..5c69747ed3 100644 --- a/org.eclipse.winery.generators.ia/src/test/java/org/eclipse/winery/generators/ia/GeneratorTest.java +++ b/org.eclipse.winery.generators.ia/src/test/java/org/eclipse/winery/generators/ia/JarAndWarGeneratorTest.java @@ -14,7 +14,6 @@ package org.eclipse.winery.generators.ia; import java.net.URL; -import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; @@ -23,24 +22,13 @@ import org.eclipse.winery.model.tosca.TOperation; import org.eclipse.winery.model.tosca.TParameter; -import org.apache.commons.io.FileUtils; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; -public class GeneratorTest { +public class JarAndWarGeneratorTest { - private static Path wd; - - @BeforeAll - public static void initialize() throws Exception { - wd = Files.createTempDirectory("IAGenerator"); - } - - @AfterAll - public static void destroy() throws Exception { - FileUtils.forceDelete(wd.toFile()); - } + @TempDir + private Path wd; @Test public void testMultipleOpsWithInOutParams() throws Exception { @@ -76,13 +64,12 @@ public void testMultipleOpsWithInOutParams() throws Exception { ) ).build(); - Generator gen = new Generator(iFace, + JarAndWarGenerator gen = new JarAndWarGenerator(iFace, "org.opentosca.ia.test", new URL("http://test.com"), "TestMultipleOpsWithInOutParams", - wd.toFile() - ); - gen.generateProject(); + wd); + gen.generateArtifact(); } @Test @@ -94,12 +81,11 @@ public void testOneOpNoParams() throws Exception { ) ).build(); - Generator gen = new Generator(iFace, + JarAndWarGenerator gen = new JarAndWarGenerator(iFace, "org.opentosca.ia.test", new URL("http://test.com"), "TestOneOpNoParams", - wd.toFile() - ); - gen.generateProject(); + wd); + gen.generateArtifact(); } } diff --git a/org.eclipse.winery.generators.ia/src/test/java/org/eclipse/winery/generators/ia/PythonGeneratorTest.java b/org.eclipse.winery.generators.ia/src/test/java/org/eclipse/winery/generators/ia/PythonGeneratorTest.java new file mode 100644 index 0000000000..998d30757d --- /dev/null +++ b/org.eclipse.winery.generators.ia/src/test/java/org/eclipse/winery/generators/ia/PythonGeneratorTest.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2013-2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + *******************************************************************************/ +package org.eclipse.winery.generators.ia; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; + +import org.eclipse.winery.model.tosca.TInterface; +import org.eclipse.winery.model.tosca.TOperation; +import org.eclipse.winery.model.tosca.TParameter; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +public class PythonGeneratorTest { + + @TempDir + + private Path wd; + + @Test + public void testMultipleOpsWithInOutParams() throws Exception { + TOperation install = new TOperation.Builder("install") + .addInputParameter( + new TParameter.Builder("VMIP", "xs:string").build() + ) + .addInputParameter( + new TParameter.Builder("DBMSUsername", "xs:string").build() + ) + .addOutputParameter( + new TParameter.Builder("Output", "xs:string").build() + ) + .build(); + + TOperation uninstall = new TOperation.Builder("uninstall") + .addInputParameter( + new TParameter.Builder("SomeLongParameterName", "xs:string").build() + ) + .addInputParameter( + new TParameter.Builder("Port", "xs:string").build() + ) + .addOutputParameter( + new TParameter.Builder("Output", "xs:string").build() + ) + .build(); + + TInterface iFace = new TInterface.Builder( + "http://www.example.org/interfaces/lifecycle", + Arrays.asList( + install, + uninstall + ) + ).build(); + + PythonGenerator gen = new PythonGenerator(iFace, + "interface", + wd); + Assertions.assertThrows(IllegalArgumentException.class, gen::generateArtifact); + } + + @Test + public void testOneOpNoParams() throws Exception { + TInterface iFace = new TInterface.Builder( + "http://www.example.org/interfaces/lifecycle", + Collections.singletonList( + new TOperation.Builder("install").build() + ) + ).build(); + + PythonGenerator gen = new PythonGenerator(iFace, + "install", + wd); + gen.generateArtifact(); + Assertions.assertTrue(Files.exists(wd.resolve("README.md"))); + Assertions.assertTrue(Files.exists(wd.resolve("install.py"))); + } +} diff --git a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/artifacts/GenericArtifactsResource.java b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/artifacts/GenericArtifactsResource.java index df99066714..76fbd0806c 100644 --- a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/artifacts/GenericArtifactsResource.java +++ b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/artifacts/GenericArtifactsResource.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012-2018 Contributors to the Eclipse Foundation + * Copyright (c) 2012-2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -27,12 +27,12 @@ import java.util.List; import java.util.Optional; +import javax.servlet.http.HttpServletResponse; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; import javax.xml.namespace.QName; @@ -103,35 +103,28 @@ public GenericArtifactsResource(Class entityResourceTClazz, Cl this.resWithNamespace = res; } - /** - * @return TImplementationArtifact | TDeploymentArtifact (XML) | URL of generated IA zip (in case of autoGenerateIA) - */ @POST @Consumes(MediaType.APPLICATION_JSON) @ApiOperation(value = "Creates a new implementation/deployment artifact. If an implementation artifact with the same name already exists, it is overridden.") @SuppressWarnings("unchecked") - public Response generateArtifact(GenerateArtifactApiData apiData, @Context UriInfo uriInfo) { + public IAGenerationReport generateArtifact(GenerateArtifactApiData apiData, @Context UriInfo uriInfo, @Context final HttpServletResponse response) throws Exception { // we assume that the parent ComponentInstance container exists final IRepository repository = RepositoryFactory.getRepository(); - if (StringUtils.isEmpty(apiData.artifactName)) { - return Response.status(Status.BAD_REQUEST).entity("Empty artifactName").build(); + throw new IllegalArgumentException("Empty artifactName"); } if (StringUtils.isEmpty(apiData.artifactType)) { if (StringUtils.isEmpty(apiData.artifactTemplateName) || StringUtils.isEmpty(apiData.artifactTemplateNamespace)) { if (StringUtils.isEmpty(apiData.artifactTemplate)) { - return Response.status(Status.BAD_REQUEST).entity("No artifact type given and no template given. Cannot guess artifact type").build(); + throw new IllegalArgumentException("No artifact type given and no template given. Cannot guess artifact type"); } } } if (!StringUtils.isEmpty(apiData.autoGenerateIA)) { - if (StringUtils.isEmpty(apiData.javaPackage)) { - return Response.status(Status.BAD_REQUEST).entity("no java package name supplied for IA auto generation.").build(); - } if (StringUtils.isEmpty(apiData.interfaceName)) { - return Response.status(Status.BAD_REQUEST).entity("no interface name supplied for IA auto generation.").build(); + throw new IllegalArgumentException("No interface name supplied for IA auto generation."); } } @@ -156,7 +149,7 @@ public Response generateArtifact(GenerateArtifactApiData apiData, @Context UriIn } catch (Exception e) { // FIXME: currently we allow a single element only. However, the content should be internally wrapped by an (arbitrary) XML element as the content will be nested in the artifact element, too LOGGER.debug("Invalid content", e); - return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build(); + throw e; } } @@ -175,7 +168,7 @@ public Response generateArtifact(GenerateArtifactApiData apiData, @Context UriIn if (StringUtils.isEmpty(apiData.artifactType)) { // derive the type from the artifact template if (artifactTemplateId == null) { - return Response.status(Status.NOT_ACCEPTABLE).entity("No artifactTemplate and no artifactType provided. Deriving the artifactType is not possible.").build(); + throw new IllegalArgumentException("No artifactTemplate and no artifactType provided. Deriving the artifactType is not possible."); } @NonNull final QName type = repository.getElement(artifactTemplateId).getType(); artifactTypeId = BackendUtils.getDefinitionsChildId(ArtifactTypeId.class, type); @@ -187,7 +180,7 @@ public Response generateArtifact(GenerateArtifactApiData apiData, @Context UriIn // do the artifact template auto creation magic if (StringUtils.isEmpty(apiData.artifactType)) { - return Response.status(Status.BAD_REQUEST).entity("Artifact template auto creation requested, but no artifact type supplied.").build(); + throw new IllegalArgumentException("Artifact template auto creation requested, but no artifact type supplied."); } artifactTypeId = BackendUtils.getDefinitionsChildId(ArtifactTypeId.class, apiData.artifactType); @@ -272,12 +265,18 @@ public Response generateArtifact(GenerateArtifactApiData apiData, @Context UriIn // TODO: Check for error, and in case one found return it RestUtils.persist(super.res); + response.setStatus(Status.CREATED.getStatusCode()); + try { + response.flushBuffer(); + } catch (Exception e) { + } if (StringUtils.isEmpty(apiData.autoGenerateIA)) { // No IA generation - return Response.created(URI.create(EncodingUtil.URLencode(apiData.artifactName))).entity(resultingArtifact).build(); + return new IAGenerationReport(URI.create(EncodingUtil.URLencode(apiData.artifactName)).toURL()); } else { + LOGGER.debug("\nArtifact API Data: ArtifactName:{},\nArtifactTemplate: {},\n ArtifactTemplateName: {}\n ArtifactType: {},\n InterfaceName: {},\nOperationName: {}", apiData.artifactName, apiData.artifactTemplate, apiData.artifactTemplateName, apiData.artifactType, apiData.interfaceName, apiData.operationName); // after everything was created, we fire up the artifact generation - return this.generateImplementationArtifact(apiData.interfaceName, apiData.javaPackage, uriInfo, artifactTemplateId); + return this.generateImplementationArtifact(apiData.interfaceName, apiData.javaPackage, uriInfo, artifactTemplateId, apiData.artifactType, apiData.operationName); } } @@ -307,21 +306,20 @@ private String generateName(EntityTypeId typeId, String interfaceName) { * Generates the implementation artifact using the implementation artifact generator. Also sets the properties * according to the requirements of OpenTOSCA. */ - private Response generateImplementationArtifact(String interfaceName, String javaPackage, UriInfo uriInfo, ArtifactTemplateId artifactTemplateId) { - + private IAGenerationReport generateImplementationArtifact(String interfaceName, String javaPackage, UriInfo uriInfo, ArtifactTemplateId artifactTemplateId, String artifactType, String operation) throws Exception { assert (this instanceof ImplementationArtifactsResource); IRepository repository = RepositoryFactory.getRepository(); QName type = RestUtils.getType(this.res); EntityTypeId typeId = getTypeId(type).orElseThrow(IllegalStateException::new); - TInterface i = findInterface(typeId, interfaceName).orElseThrow(IllegalStateException::new); + TInterface tInterface = findInterface(typeId, interfaceName).orElseThrow(IllegalStateException::new); Path workingDir; try { workingDir = Files.createTempDirectory("winery"); } catch (IOException e2) { LOGGER.debug("Could not create temporary directory", e2); - return Response.serverError().entity("Could not create temporary directory").build(); + throw new IOException("Could not create temporary directory"); } URI artifactTemplateFilesUri = uriInfo.getBaseUri().resolve(RestUtils.getAbsoluteURL(artifactTemplateId)).resolve("files"); @@ -330,33 +328,42 @@ private Response generateImplementationArtifact(String interfaceName, String jav artifactTemplateFilesUrl = artifactTemplateFilesUri.toURL(); } catch (MalformedURLException e2) { LOGGER.debug("Could not convert URI to URL", e2); - return Response.serverError().entity("Could not convert URI to URL").build(); + throw new MalformedURLException("Could not convert URI to URL"); } - + + IAGenerationReport result = new IAGenerationReport(); String name = this.generateName(typeId, interfaceName); - Generator gen = new Generator(i, javaPackage, artifactTemplateFilesUrl, name, workingDir.toFile()); + Generator gen = Generator.getGenerator(artifactType, tInterface, javaPackage, artifactTemplateFilesUrl, name, workingDir, operation); Path targetPath; try { - targetPath = gen.generateProject(); - } catch (Exception e) { - LOGGER.debug("IA generator failed", e); - return Response.serverError().entity("IA generator failed").build(); - } - - DirectoryId fileDir = new ArtifactTemplateSourceDirectoryId(artifactTemplateId); - try { + targetPath = gen.generateArtifact(); + DirectoryId fileDir = new ArtifactTemplateSourceDirectoryId(artifactTemplateId); BackendUtils.importDirectory(targetPath, repository, fileDir); - } catch (IOException e) { + } catch (IllegalArgumentException iaEx) { + LOGGER.debug("IA stub generation failed", iaEx); + result.warning = "IA stub generation failed, as selected artifact type does not support interface level generation."; + } catch (Exception e) { throw new WebApplicationException(e); } - + // clean up FileUtils.forceDelete(workingDir); this.storeProperties(artifactTemplateId, typeId, name); - URI url = uriInfo.getBaseUri().resolve(Util.getUrlPath(artifactTemplateId)); - return Response.created(url).build(); + result.artifactTemplate = uriInfo.getBaseUri().resolve(Util.getUrlPath(artifactTemplateId)).toURL(); + return result; + } + + private static class IAGenerationReport { + public String warning = ""; + public URL artifactTemplate; + + public IAGenerationReport() { + } + public IAGenerationReport(URL artifactTemplate) { + this.artifactTemplate = artifactTemplate; + } } private Optional getTypeId(QName type) { diff --git a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/edmm/EdmmResource.java b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/edmm/EdmmResource.java index 1dc6142322..92126f20c3 100644 --- a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/edmm/EdmmResource.java +++ b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/edmm/EdmmResource.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021 Contributors to the Eclipse Foundation + * Copyright (c) 2021-2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -15,7 +15,6 @@ import java.io.ByteArrayInputStream; import java.io.File; -import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -25,6 +24,7 @@ import java.util.UUID; import java.util.stream.Collectors; +import javax.servlet.http.HttpServletResponse; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; @@ -185,7 +185,7 @@ public Response getOneToOneMap() { @POST @Consumes(MediaType.TEXT_PLAIN) @Path("create-placeholders-scripts") - public Response createPlaceholders(String componentType, @Context UriInfo uriInfo) throws IOException { + public Response createPlaceholders(String componentType, @Context UriInfo uriInfo, @Context HttpServletResponse response) throws Exception { // adding the interface to the component node type NodeTypeResource nodeTypeResource = new NodeTypesResource().getComponentInstanceResource(EncodingUtil.URLencode(NODE_TYPES), componentType); @@ -251,7 +251,7 @@ public Response createPlaceholders(String componentType, @Context UriInfo uriInf artifactApiData.artifactTemplate.equals(implementationArtifact.getArtifactRef().toString()); }); if (!implementationResourceExists) { - nodeTypeImplementationResource.getImplementationArtifacts().generateArtifact(artifactApiData, uriInfo); + nodeTypeImplementationResource.getImplementationArtifacts().generateArtifact(artifactApiData, uriInfo, response); } } return Response.status(Response.Status.CREATED).build();