From d2b0104d1c04984c133c7342896ccf97b143a5be Mon Sep 17 00:00:00 2001 From: Jakob Haastert Date: Thu, 23 Jan 2025 13:19:42 +0100 Subject: [PATCH 1/3] fixed a few issues, made the mojo work, reverted the indentation for untouched files --- .../.project | 6 + .../dependencies.txt | 27 + .../osgi.spviz | 45 +- .../osgi.spvizmodel | 7 +- .../pom.xml | 30 + .../spviz/osgi/generate/ReadProjectFiles.java | 670 +++++++++++------- .../spviz/osgi/generate/mvn/MavenMojo.java | 66 ++ .../spviz/osgi/generate/util/EdgeType.java | 18 + .../spviz/osgi/generate/util/FileUtil.java | 144 ++++ .../{ => util}/ReadProjectFilesUtility.java | 4 +- 10 files changed, 739 insertions(+), 278 deletions(-) create mode 100644 osgi/de.cau.cs.kieler.spviz.osgi.generate/dependencies.txt rename osgi/{ => de.cau.cs.kieler.spviz.osgi.generate}/osgi.spviz (51%) rename osgi/{ => de.cau.cs.kieler.spviz.osgi.generate}/osgi.spvizmodel (73%) create mode 100644 osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/mvn/MavenMojo.java create mode 100644 osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/util/EdgeType.java create mode 100644 osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/util/FileUtil.java rename osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/{ => util}/ReadProjectFilesUtility.java (90%) diff --git a/osgi/de.cau.cs.kieler.spviz.osgi.generate/.project b/osgi/de.cau.cs.kieler.spviz.osgi.generate/.project index 9a7b7c6..0a36457 100644 --- a/osgi/de.cau.cs.kieler.spviz.osgi.generate/.project +++ b/osgi/de.cau.cs.kieler.spviz.osgi.generate/.project @@ -5,6 +5,11 @@ + + org.eclipse.xtext.ui.shared.xtextBuilder + + + org.eclipse.jdt.core.javabuilder @@ -19,5 +24,6 @@ org.eclipse.m2e.core.maven2Nature org.eclipse.jdt.core.javanature + org.eclipse.xtext.ui.shared.xtextNature diff --git a/osgi/de.cau.cs.kieler.spviz.osgi.generate/dependencies.txt b/osgi/de.cau.cs.kieler.spviz.osgi.generate/dependencies.txt new file mode 100644 index 0000000..8f1bf1a --- /dev/null +++ b/osgi/de.cau.cs.kieler.spviz.osgi.generate/dependencies.txt @@ -0,0 +1,27 @@ +de.cau.cs.kieler.spviz.osgi.generate:de.cau.cs.kieler.spviz.osgi.generate:maven-plugin:0.0.1-SNAPSHOT ++- org.apache.maven:maven-plugin-api:jar:3.9.6:compile +| +- org.apache.maven:maven-model:jar:3.9.6:compile +| +- org.apache.maven:maven-artifact:jar:3.9.6:compile +| | \- org.apache.commons:commons-lang3:jar:3.12.0:compile +| +- org.eclipse.sisu:org.eclipse.sisu.plexus:jar:0.9.0.M2:compile +| | +- javax.annotation:javax.annotation-api:jar:1.2:compile +| | +- org.eclipse.sisu:org.eclipse.sisu.inject:jar:0.9.0.M2:compile +| | \- org.codehaus.plexus:plexus-component-annotations:jar:2.1.0:compile +| +- org.codehaus.plexus:plexus-utils:jar:3.5.1:compile +| \- org.codehaus.plexus:plexus-classworlds:jar:2.7.0:compile ++- org.apache.maven.plugin-tools:maven-plugin-annotations:jar:3.9.0:compile ++- org.eclipse.emf:org.eclipse.emf.ecore.xmi:jar:2.16.0:compile +| \- org.eclipse.emf:org.eclipse.emf.ecore:jar:2.38.0:compile (version selected from constraint [2.18.0,3.0.0)) +| \- org.eclipse.emf:org.eclipse.emf.common:jar:2.40.0:compile (version selected from constraint [2.40.0,3.0.0)) ++- de.cau.cs.kieler.spviz.osgi:de.cau.cs.kieler.spviz.osgi.model:jar:0.1.0-SNAPSHOT:compile +| +- com.google.guava:guava:jar:32.1.3-jre:compile +| | +- com.google.guava:listenablefuture:jar:9999.0-empty-to-avoid-conflict-with-guava:compile +| | +- com.google.code.findbugs:jsr305:jar:3.0.2:compile +| | +- org.checkerframework:checker-qual:jar:3.37.0:compile +| | +- com.google.errorprone:error_prone_annotations:jar:2.21.1:compile +| | \- com.google.j2objc:j2objc-annotations:jar:2.8:compile +| +- com.google.guava:failureaccess:jar:1.0.2:compile +| \- org.osgi:org.osgi.service.prefs:jar:1.1.2:compile +| \- org.osgi:osgi.annotation:jar:8.0.1:compile ++- info.picocli:picocli:jar:4.6.3:compile +\- commons-io:commons-io:jar:2.11.0:compile diff --git a/osgi/osgi.spviz b/osgi/de.cau.cs.kieler.spviz.osgi.generate/osgi.spviz similarity index 51% rename from osgi/osgi.spviz rename to osgi/de.cau.cs.kieler.spviz.osgi.generate/osgi.spviz index e5ed160..80007a3 100644 --- a/osgi/osgi.spviz +++ b/osgi/de.cau.cs.kieler.spviz.osgi.generate/osgi.spviz @@ -14,19 +14,32 @@ SPViz OSGiViz { connect OSGi.ServiceComponent.Required via OSGi.Bundle in Services connect OSGi.ServiceInterface.ProvidedBy via OSGi.Bundle in Services } - BundleDependencies { + + RequireBundleDependencies { + show OSGi.Bundle + connect OSGi.Bundle.Dependency + } + + BundlePackageDependencies { + show OSGi.Bundle + show OSGi.Package + connect OSGi.Bundle.PackageExportDependency + connect OSGi.Bundle.PackageImportDependency + connect OSGi.Bundle.PackageDependency + } + + AllDependencies { show OSGi.Bundle connect OSGi.Bundle.Dependency -// connect OSGi.Bundle via OSGi.Bundle.PackageDependency + connect OSGi.Bundle.PackageDependency } + Products { show OSGi.Product -// connect OSGi.Bundle.Dependency via OSGi.Product>OSGi.Feature>OSGi.Bundle } Features { show OSGi.Feature - connect OSGi.Bundle.Dependency via OSGi.Feature in BundleDependencies - // connect Feature to Feature via source Feature>Bundle and target Feature>Bundle >Dep + connect OSGi.Bundle.Dependency via OSGi.Feature in RequireBundleDependencies } OSGi.Product shows { @@ -34,15 +47,33 @@ SPViz OSGiViz { OSGi.ServiceInterface from OSGi.Product>OSGi.Bundle>OSGi.ServiceInterface OSGi.ServiceComponent from OSGi.Product>OSGi.Bundle>OSGi.ServiceComponent } - BundleDependencies with { + RequireBundleDependencies with { OSGi.Bundle from OSGi.Product>OSGi.Bundle } + BundlePackageDependencies with { + OSGi.Bundle from OSGi.Product>OSGi.Bundle + OSGi.Package from OSGi.Product>OSGi.Package + } + AllDependencies with { + OSGi.Bundle from OSGi.Product>OSGi.Bundle + } + Features with { + OSGi.Feature from OSGi.Product>OSGi.Feature + } } OSGi.Feature shows { - BundleDependencies with { + RequireBundleDependencies with { OSGi.Bundle from OSGi.Feature>OSGi.Bundle } + BundlePackageDependencies with { + OSGi.Bundle from OSGi.Feature>OSGi.Bundle + OSGi.Package from OSGi.Feature>OSGi.Bundle>OSGi.Package + } + AllDependencies with { + OSGi.Bundle from OSGi.Feature>OSGi.Bundle + } + } OSGi.Bundle shows { diff --git a/osgi/osgi.spvizmodel b/osgi/de.cau.cs.kieler.spviz.osgi.generate/osgi.spvizmodel similarity index 73% rename from osgi/osgi.spvizmodel rename to osgi/de.cau.cs.kieler.spviz.osgi.generate/osgi.spvizmodel index 9ceb846..d282353 100644 --- a/osgi/osgi.spvizmodel +++ b/osgi/de.cau.cs.kieler.spviz.osgi.generate/osgi.spvizmodel @@ -4,16 +4,19 @@ SPVizModel OSGi { Product { contains Feature contains Bundle + contains Package } Feature { contains Bundle } Bundle { Dependency connects Bundle - PackageDependency connects Package + PackageExportDependency connects Package + PackageImportDependency connects Package + PackageDependency connects Bundle + contains Package contains ServiceInterface contains ServiceComponent - contains Package } ServiceInterface { ProvidedBy connects ServiceComponent diff --git a/osgi/de.cau.cs.kieler.spviz.osgi.generate/pom.xml b/osgi/de.cau.cs.kieler.spviz.osgi.generate/pom.xml index 56a0739..4508e27 100644 --- a/osgi/de.cau.cs.kieler.spviz.osgi.generate/pom.xml +++ b/osgi/de.cau.cs.kieler.spviz.osgi.generate/pom.xml @@ -14,8 +14,24 @@ de.cau.cs.kieler.spviz.osgi.generate de.cau.cs.kieler.spviz.osgi.generate 0.0.1-SNAPSHOT + maven-plugin + + org.apache.maven + maven-plugin-api + 3.9.6 + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.9.0 + + + org.eclipse.emf + org.eclipse.emf.ecore.xmi + 2.16.0 + org.eclipse.emf org.eclipse.emf.ecore.xmi @@ -50,6 +66,20 @@ + + org.apache.maven.plugins + maven-plugin-plugin + 3.9.0 + + + default-descriptor + process-classes + + descriptor + + + + maven-compiler-plugin 3.10.0 diff --git a/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/ReadProjectFiles.java b/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/ReadProjectFiles.java index 7745c34..fad057b 100644 --- a/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/ReadProjectFiles.java +++ b/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/ReadProjectFiles.java @@ -3,17 +3,17 @@ // A part of Kieler // https://github.com/kieler // -// Copyright (c) 2018-2022 by +// Copyright (c) 2018-2025 by // Scheidt & Bachmann System Technik GmbH, 24145 Kiel -// and +// and // + Christian-Albrechts-University of Kiel -// + Department of Computer Science -// + Real-Time and Embedded Systems Group +// + Department of Computer Science +// + Real-Time and Embedded Systems Group // // 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. -// +// // SPDX-License-Identifier: EPL-2.0 // // ****************************************************************************** @@ -28,9 +28,11 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.jar.Attributes; import java.util.jar.Manifest; @@ -43,6 +45,9 @@ import org.w3c.dom.NodeList; import org.xml.sax.SAXException; +import de.cau.cs.kieler.spviz.osgi.generate.util.EdgeType; +import de.cau.cs.kieler.spviz.osgi.generate.util.FileUtil; +import de.cau.cs.kieler.spviz.osgi.generate.util.ReadProjectFilesUtility; import de.cau.cs.kieler.spviz.osgi.model.Bundle; import de.cau.cs.kieler.spviz.osgi.model.Feature; import de.cau.cs.kieler.spviz.osgi.model.OSGiFactory; @@ -74,10 +79,14 @@ public class ReadProjectFiles { static final java.lang.System.Logger LOGGER = System.getLogger(OsgiModelDataGenerator.class.getName()); final OSGiProject project = OSGiFactory.eINSTANCE.createOSGiProject(); -// private final List packageDependencies = new ArrayList(); + private final List filePaths = new ArrayList(); + private final Map componentPaths = new HashMap<>(); + private List tempFolders = new ArrayList<>(); + + private List>> labeledPackageDependencyEdges = new ArrayList<>(); /** * Generates the HashMaps with Data for bundles, features, services and @@ -89,34 +98,161 @@ public class ReadProjectFiles { */ public OSGiProject generateData(final File projectPath, final String projectName) { project.setProjectName(projectName); - // Parsing of manifest data filePaths.clear(); findFiles(StaticVariables.MANIFEST_FILE, projectPath); - for (final Path manifestPath : filePaths) { - extractBundleData(manifestPath); + try { + for (final Path manifestPath : filePaths) { + extractBundleData(manifestPath); + } + + // parsing of feature data + filePaths.clear(); + findFiles(StaticVariables.FEATURE_FILE, projectPath); + for (final Path featurePath : filePaths) { + extractFeatureData(featurePath); + } + + // parsing of service data + project.getServiceComponents().forEach(elem -> extractServiceData(elem)); + + // parsing of product data + filePaths.clear(); + findFiles(StaticVariables.PRODUCT_FILE, projectPath); + for (final Path productPath : filePaths) { + extractProductData(productPath); + } + connectBundlesViaPackages(); + tempFolders.forEach(f -> FileUtil.deleteTempFolder(f)); + for (LabeledEdge> edge : labeledPackageDependencyEdges) { + // This map contains information about the origin and amount of imported + // packages for the edge. + // In the future it can be used to label the edges like in OSGiViz. + Map extraInfo = edge.getExtraInfo(); + } + } catch (Exception e) { + // In case something goes wrong, delete temporary folders, then explode. + tempFolders.forEach(f -> FileUtil.deleteTempFolder(f)); + throw e; } - - // parsing of feature data - filePaths.clear(); - findFiles(StaticVariables.FEATURE_FILE, projectPath); - for (final Path featurePath : filePaths) { - extractFeatureData(featurePath); + return project; + } + + private void connectBundlesViaPackages() { + project.getPackages().forEach(p -> connectBundlesViaSinglePackageDependency(p)); + } + + private void connectBundlesViaSinglePackageDependency(Package inputPackage) { + if (inputPackage.getConnectingPackageExportDependencyBundles().isEmpty()) { + LOGGER.log(System.Logger.Level.DEBUG, inputPackage.getName() + " has no exported package"); + return; } - // parsing of service data - project.getServiceComponents().forEach(elem -> extractServiceData(elem)); - - // parsing of product data - filePaths.clear(); - findFiles(StaticVariables.PRODUCT_FILE, projectPath); - for (final Path productPath : filePaths) { - extractProductData(productPath); + Bundle exportedBundle = inputPackage.getConnectingPackageExportDependencyBundles().get(0); + inputPackage.getConnectingPackageImportDependencyBundles().forEach(bundle -> { + bundle.getConnectedPackageDependencyBundles().add(exportedBundle); + getCommonProductsForBundles(bundle, exportedBundle) + .forEach(product -> addOrUpdatePackageDependencyEdgeLabel(bundle, exportedBundle, product)); + }); + + } + + // TODO: Integrate this label to package edges once edge labels are possible. + private String getLabel(Product prod, Map map) { + return String.format("%s: %s packages", prod.getName(), map.get(prod)); + } + + private void addOrUpdatePackageDependencyEdgeLabel(Bundle bundleA, Bundle bundleB, Product product) { + Optional>> optionalLabelEntry = labeledPackageDependencyEdges + .stream() // + .filter(l -> l.getSource().equals(bundleA)) // + .filter(l -> l.getTarget().equals(bundleB)).findFirst(); + optionalLabelEntry.ifPresent(entr -> { + if (entr.getExtraInfo().keySet().contains(product)) { + entr.getExtraInfo().put(product, entr.getExtraInfo().get(product) + 1); + } else { + entr.getExtraInfo().put(product, 1); + } + return; + }); + Map productPackageCountMap = new HashMap<>(); + productPackageCountMap.put(product, 1); + labeledPackageDependencyEdges + .add(new LabeledEdge<>(EdgeType.PACKAGE_DEPENDENCY, bundleA, bundleB, productPackageCountMap)); + + } + + private Set getCommonProductsForBundles(Bundle bundleA, Bundle bundleB) { + Set outputSet = new HashSet<>(); + project.getProducts().stream() // + .filter(pr -> pr.getBundles().contains(bundleA) && pr.getBundles().contains(bundleB)) // + .forEach(prod -> outputSet.add(prod)); + return outputSet; + } + + /** + * Returns the value of the attribute "key" out of an attribute list and removes + * it from the list. + * + * @param attributes the list of attributes + * @param key the attribute asked for + * @return the value of the attribute, if the attribute does not exist, return + * is null. + */ + private static String getAndRemove(final Attributes attributes, final String key) { + final String value = attributes.getValue(key); + attributes.remove(new Attributes.Name(key)); + if (null == value) { + return StaticVariables.NOT_SET; } - - - return project; + return value.split(";")[0]; + } + + /** + * Returns the value (comma separated list) of the attribute "key" out of an + * attribute list and removes it from the list. Splits the comma separated + * String of Strings into a list of Strings. + * + * @param attributes + * @param key + * @return + */ + private static List getList(final Attributes attributes, final String key) { + final String list = attributes.getValue(key); + attributes.remove(new Attributes.Name(key)); + final List result = new ArrayList(); + if (null == list) { + return result; + } + for (final String b : list.replaceAll("\"(.*?)\"", "") // remove all characters between '"' + .replaceAll("\\s", "") // remove all whitespaces + .split(",")) { + result.add(b.split(";")[0]); + } + return result; + } + + private void addServiceComponentFiles(final Path manifestPath, Path bundleRoot, final Bundle bundle) { + final File serviceComponentsFolder = new File(String.format("%s/OSGI-INF/", bundleRoot)); + if (!serviceComponentsFolder.exists()) { + LOGGER.log(System.Logger.Level.INFO, + "The MANIFEST-INF folder and the OSGI-INF folder are not in the same path." + + manifestPath.toString()); + } + final File[] serviceComponentFiles = serviceComponentsFolder + .listFiles((dir, name) -> name.toLowerCase().endsWith(".xml")); + if (serviceComponentFiles != null) { + for (final File serviceComponentFile : serviceComponentFiles) { + + String componentName = serviceComponentFile.getName().replace(".xml", StaticVariables.EMPTY_STRING); + final ServiceComponent serviceComponent = getOrCreateServiceComponent(componentName); + serviceComponent.getBundles().add(bundle); + bundle.getServiceComponents().add(serviceComponent); + componentPaths.put(serviceComponent, + FilenameUtils.separatorsToUnix(serviceComponentFile.getAbsolutePath())); + } + } } /** @@ -131,50 +267,42 @@ private void extractBundleData(final Path manifestPath) { if (manifestFolder != null) { bundleRoot = manifestFolder.getParent(); } - - try (final InputStream is = new FileInputStream(manifestPath.toString())){ + try (final InputStream is = new FileInputStream(manifestPath.toString())) { final Manifest manifest = new Manifest(is); + try { + if (bundleRoot != null && !bundleRoot.resolve("OSGI-INF").toFile().exists()) { + FileUtil.createTemporaryOSGiInfDirectory(bundleRoot, manifest); + tempFolders.add(bundleRoot.toFile()); + } + } catch (IOException e) { + FileUtil.deleteTempFolder(bundleRoot.toFile()); + + } final Attributes attributes = manifest.getMainAttributes(); - + symbolicName = getAndRemove(attributes, StaticVariables.BUNDLE_SYMBOLIC_NAME); - + // check, if bundle is already existing final Bundle bundle = getOrCreateBundle(symbolicName); bundle.setExternal(false); extractPackages(bundle, attributes); - + // check all required bundles and create them, if not existing for (final String requiredBundleName : getList(attributes, StaticVariables.REQUIRE_BUNDLE)) { final Bundle requiredBundle = getOrCreateBundle(requiredBundleName); requiredBundle.getConnectingDependencyBundles().add(bundle); } - + final List serviceComponents = getList(attributes, StaticVariables.SERVICE_COMPONENT); - + if (serviceComponents.contains("OSGI-INF/*.xml")) { - if (!new File(bundleRoot + "/OSGI-INF").exists()) { - LOGGER.log(System.Logger.Level.INFO, "The MANIFEST-INF folder and the OSGI-INF folder are not in the same path." - + manifestPath.toString()); - } - final File serviceComponentsFolder = new File(bundleRoot + "/OSGI-INF/"); - final File[] serviceComponentFiles = serviceComponentsFolder - .listFiles((dir, name) -> name.toLowerCase().endsWith(".xml")); - - if (serviceComponentFiles != null) { - for (final File serviceComponentFile : serviceComponentFiles) { - - String componentName = serviceComponentFile.getName() - .replace(".xml", StaticVariables.EMPTY_STRING); - final ServiceComponent serviceComponent = getOrCreateServiceComponent(componentName); - serviceComponent.getBundles().add(bundle); - bundle.getServiceComponents().add(serviceComponent); - componentPaths.put(serviceComponent, FilenameUtils.separatorsToUnix(serviceComponentFile.getAbsolutePath())); - } - } + addServiceComponentFiles(manifestPath, bundleRoot, bundle); + } else if (serviceComponents.contains("/OSGI-INF/*.xml/")) { + addServiceComponentFiles(manifestPath, bundleRoot, bundle); } else { for (final String service : serviceComponents) { - final String serviceName = service.replace(".xml", StaticVariables.EMPTY_STRING).replace( - "OSGI-INF/", StaticVariables.EMPTY_STRING); + final String serviceName = service.replace(".xml", StaticVariables.EMPTY_STRING) + .replace("OSGI-INF/", StaticVariables.EMPTY_STRING); final ServiceComponent serviceComponent = OSGiFactory.eINSTANCE.createServiceComponent(); serviceComponent.setName(serviceName); serviceComponent.setEcoreId(StaticVariables.SERVICE_COMPONENT_PREFIX + toAscii(serviceName)); @@ -182,116 +310,158 @@ private void extractBundleData(final Path manifestPath) { project.getServiceComponents().add(serviceComponent); if (service.contains(StaticVariables.XML_FILE) && !service.contains("*.xml") && bundleRoot != null) { - componentPaths.put(serviceComponent, FilenameUtils.separatorsToUnix( - bundleRoot + service.replace("OSGI-INF/", "/OSGI-INF/"))); + componentPaths.put(serviceComponent, FilenameUtils + .separatorsToUnix(bundleRoot + service.replace("OSGI-INF/", "/OSGI-INF/"))); } } } - + } catch (final IOException e) { LOGGER.log(System.Logger.Level.ERROR, "There was an error with reading the manifest file " + e); } } /** - * Returns the bundle with the given identifying name. Will be created if the - * bundle does not exist yet. + * extracts information out of the feature xml file in featurepath, and adds it + * to the feature HashMap * - * @param name The unique name of the bundle. - * @return The bundle for the given name. + * @param featurePath is the path to the feature.xml file */ - private Bundle getOrCreateBundle(final String name) { - final Optional bundleAlreadyPresent = project.getBundles()// - .stream()// - .filter(elem -> elem.getEcoreId().equals(StaticVariables.BUNDLE_PREFIX + toAscii(name)))// - .findFirst(); - if (bundleAlreadyPresent.isPresent()) { - return bundleAlreadyPresent.get(); - } else { - final Bundle bundle = OSGiFactory.eINSTANCE.createBundle(); - bundle.setName(name); - bundle.setEcoreId(StaticVariables.BUNDLE_PREFIX + toAscii(name)); - bundle.setExternal(true); - project.getBundles().add(bundle); - return bundle; + private void extractFeatureData(final Path featurePath) { + final File xmlFile = featurePath.toFile(); + final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder; + + try { + dBuilder = dbFactory.newDocumentBuilder(); + final Document doc = dBuilder.parse(xmlFile); + final NodeList pluginNodeList = doc.getElementsByTagName(StaticVariables.PLUGIN); + + String featureName = doc.getDocumentElement().getAttribute(StaticVariables.ID); + final Feature feature = getOrCreateFeature(featureName); + feature.setExternal(false); + + for (int x = 0, size = pluginNodeList.getLength(); x < size; x++) { + final String plugin = pluginNodeList.item(x).getAttributes().getNamedItem(StaticVariables.ID) + .getNodeValue(); + feature.getBundles().add(getOrCreateBundle(plugin)); + } + + } catch (ParserConfigurationException | SAXException | IOException e) { + LOGGER.log(System.Logger.Level.ERROR, "There was an error with reading the feature.xml file " + e); } } /** - * Returns the feature with the given identifying name. Will be created if the - * feature does not exist yet. + * Generates a list of {@link PackageObject} for a bundle. Packages can be + * exported or imported packages of a bundle * - * @param name The unique name of the feature. - * @return The feature for the given name. + * @param bundle is the bundle which exports/imports the packages + * @param attributes is the String of packages + * @param key describes whether the packages are imported or exported + * @return */ - private Feature getOrCreateFeature(final String name) { - final Optional featureAlreadyPresent = project.getFeatures()// - .stream()// - .filter(elem -> elem.getEcoreId().equals(StaticVariables.FEATURE_PREFIX + toAscii(name)))// - .findFirst(); - if (featureAlreadyPresent.isPresent()) { - return featureAlreadyPresent.get(); - } else { - final Feature feature = OSGiFactory.eINSTANCE.createFeature(); - feature.setName(name); - feature.setEcoreId(StaticVariables.FEATURE_PREFIX + toAscii(name)); - feature.setExternal(true); - project.getFeatures().add(feature); - return feature; + private void extractPackages(final Bundle bundle, final Attributes attributes) { + for (final String importOrExport : new String[] { StaticVariables.EXPORT_PACKAGE, + StaticVariables.IMPORT_PACKAGE }) { + + final String packageIds = attributes.getValue(importOrExport); + attributes.remove(new Attributes.Name(importOrExport)); + + if (null == packageIds) { + continue; + } + + for (final String b : packageIds.replaceAll("\\s", "").replaceAll("\"(.*?)\"", "").split(",")) { + final String packageName = b.split(";")[0]; + + if (importOrExport.equals(StaticVariables.EXPORT_PACKAGE)) { + addExportedPackage(bundle, packageName); + } else { + addImportedPackage(bundle, packageName); + } + } } } - - /** - * Returns the service component with the given identifying name. Will be created if the - * component does not exist yet. - * - * @param name The unique name of the service component. - * @return The service component for the given name. - */ - private ServiceComponent getOrCreateServiceComponent(final String name) { - final Optional serviceComponentOptional = project.getServiceComponents()// + + private void addImportedPackage(final Bundle bundle, final String packageName) { + final Optional ownProjectPackage = project.getPackages()// .stream()// - .filter(elem -> elem.getName().equals(StaticVariables.SERVICE_COMPONENT_PREFIX + toAscii(name)))// + .filter(elem -> elem.getName().equals(packageName))// .findFirst(); - if (serviceComponentOptional.isPresent()) { - return serviceComponentOptional.get(); + if (ownProjectPackage.isPresent()) { + ownProjectPackage.get().getConnectingPackageImportDependencyBundles().add(bundle); + bundle.getConnectedPackageImportDependencyPackages().add(ownProjectPackage.get()); } else { - final ServiceComponent serviceComponent = OSGiFactory.eINSTANCE.createServiceComponent(); - serviceComponent.setName(name); - serviceComponent.setEcoreId(StaticVariables.SERVICE_COMPONENT_PREFIX + toAscii(name)); - project.getServiceComponents().add(serviceComponent); - return serviceComponent; + final Package newImportedPackage = OSGiFactory.eINSTANCE.createPackage(); + newImportedPackage.setName(packageName); + newImportedPackage.setEcoreId(StaticVariables.PACKAGE_PREFIX + toAscii(packageName)); + newImportedPackage.getConnectingPackageImportDependencyBundles().add(bundle); + // newImportedPackage.getConnectedImportedByBundles().add(bundle); + bundle.getConnectedPackageImportDependencyPackages().add(newImportedPackage); + project.getPackages().add(newImportedPackage); } } + private void addExportedPackage(final Bundle bundle, final String packageName) { + project.getPackages().stream().filter(p -> p.getName().equals(packageName)).findFirst() + .ifPresent(exportedPackage -> { + bundle.getConnectedPackageExportDependencyPackages().add(exportedPackage); + bundle.getPackages().add(exportedPackage); + return; + }); + final Package newExportedPackage = OSGiFactory.eINSTANCE.createPackage(); + newExportedPackage.setName(packageName); + newExportedPackage.setEcoreId(StaticVariables.PACKAGE_PREFIX + bundle.getEcoreId() + toAscii(packageName)); + newExportedPackage.getConnectingPackageExportDependencyBundles().add(bundle); + bundle.getConnectedPackageExportDependencyPackages().add(newExportedPackage); + project.getPackages().add(newExportedPackage); + project.getProducts().stream() // + .filter(prod -> prod.getBundles().contains(bundle)) // + .forEach(productWithPackage -> productWithPackage.getPackages().add(newExportedPackage)); + bundle.getPackages().add(newExportedPackage); + } + /** - * extracts information out of the feature xml file in featurepath, and adds it - * to the feature HashMap + * extracts information out of the product file, and adds it to productData. * - * @param featurePath is the path to the feature.xml file + * @param productPath is the path to the product file */ - private void extractFeatureData(final Path featurePath) { - final File xmlFile = featurePath.toFile(); + private void extractProductData(final Path productPath) { final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder dBuilder; - try { dBuilder = dbFactory.newDocumentBuilder(); - final Document doc = dBuilder.parse(xmlFile); - final NodeList pluginNodeList = doc.getElementsByTagName(StaticVariables.PLUGIN); - - String featureName = doc.getDocumentElement().getAttribute(StaticVariables.ID); - final Feature feature = getOrCreateFeature(featureName); - feature.setExternal(false); - - for (int x = 0, size = pluginNodeList.getLength(); x < size; x++) { - final String plugin = pluginNodeList.item(x).getAttributes().getNamedItem(StaticVariables.ID) + final Document doc = dBuilder.parse(productPath.toString()); + String productName = doc.getDocumentElement().getAttribute(StaticVariables.UNIQUE_ID); + if (productName.equals("")) { + productName = productPath.getFileName().toString(); + } + final Product product = OSGiFactory.eINSTANCE.createProduct(); + product.setName(productName); + product.setEcoreId(StaticVariables.PRODUCT_PREFIX + toAscii(productName)); + + project.getProducts().add(product); + + final NodeList featureNodeList = doc.getElementsByTagName(StaticVariables.FEATURE); + for (int x = 0, size = featureNodeList.getLength(); x < size; x++) { + final String featureName = featureNodeList.item(x).getAttributes().getNamedItem(StaticVariables.ID) .getNodeValue(); - feature.getBundles().add(getOrCreateBundle(plugin)); + final Feature feature = getOrCreateFeature(featureName); + feature.getProducts().add(product); + product.getFeatures().add(feature); + } + + final NodeList bundleNodeList = doc.getElementsByTagName(StaticVariables.PLUGIN); + for (int x = 0, size = bundleNodeList.getLength(); x < size; x++) { + final String bundleName = bundleNodeList.item(x).getAttributes().getNamedItem(StaticVariables.ID) + .getNodeValue(); + final Bundle bundle = getOrCreateBundle(bundleName); + bundle.getProducts().add(product); + product.getBundles().add(bundle); } - } catch (ParserConfigurationException | SAXException | IOException e) { - LOGGER.log(System.Logger.Level.ERROR, "There was an error with reading the feature.xml file " + e); + LOGGER.log(System.Logger.Level.ERROR, "There was an error with reading the product xml file " + e); } } @@ -306,18 +476,16 @@ private void extractServiceData(final ServiceComponent serviceComponent) { final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder dBuilder; dBuilder = dbFactory.newDocumentBuilder(); - final File xmlFile = new File(componentPaths.get(serviceComponent)); final Document doc = dBuilder.parse(xmlFile); final NodeList referenceList = doc.getElementsByTagName(StaticVariables.REFERENCE); final NodeList interfaceList = doc.getElementsByTagName(StaticVariables.PROVIDE); - + // read interface if (interfaceList.getLength() != 0) { for (int x = 0, size = interfaceList.getLength(); x < size; x++) { final String interfaceName = interfaceList.item(x).getAttributes() .getNamedItem(StaticVariables.INTERFACE).getNodeValue(); - final Optional serviceInterfaceOptional = project.getServiceInterfaces()// .stream()// .filter(elem -> elem.getName().equals(interfaceName))// @@ -340,12 +508,12 @@ private void extractServiceData(final ServiceComponent serviceComponent) { // read references for (int x = 0, size = referenceList.getLength(); x < size; x++) { - + final String interfaceName = (referenceList.item(x).getAttributes() .getNamedItem(StaticVariables.REFERENCE_INTERFACE) == null ? StaticVariables.NOT_SET : referenceList.item(x).getAttributes() .getNamedItem(StaticVariables.REFERENCE_INTERFACE).getNodeValue()); - + // check if interface is already existing, else create it. final Optional serviceInterfaceOptional = project.getServiceInterfaces()// .stream()// @@ -366,51 +534,10 @@ private void extractServiceData(final ServiceComponent serviceComponent) { } } } catch (ParserConfigurationException | SAXException | IOException e) { - LOGGER.log(System.Logger.Level.ERROR, "There was an error with reading the service xml file " + e); - } - } - - /** - * extracts information out of the product file, and adds it to productData. - * - * @param productPath is the path to the product file - */ - private void extractProductData(final Path productPath) { - final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder dBuilder; - try { - dBuilder = dbFactory.newDocumentBuilder(); - final Document doc = dBuilder.parse(productPath.toString()); - final String productName = doc.getDocumentElement().getAttribute(StaticVariables.UNIQUE_ID); - - final Product product = OSGiFactory.eINSTANCE.createProduct(); - product.setName(productName); - product.setEcoreId(StaticVariables.PRODUCT_PREFIX + toAscii(productName)); - - project.getProducts().add(product); - - final NodeList featureNodeList = doc.getElementsByTagName(StaticVariables.FEATURE); - for (int x = 0, size = featureNodeList.getLength(); x < size; x++) { - final String featureName = featureNodeList.item(x).getAttributes().getNamedItem(StaticVariables.ID) - .getNodeValue(); - final Feature feature = getOrCreateFeature(featureName); - feature.getProducts().add(product); - product.getFeatures().add(feature); - } - - final NodeList bundleNodeList = doc.getElementsByTagName(StaticVariables.PLUGIN); - for (int x = 0, size = bundleNodeList.getLength(); x < size; x++) { - final String bundleName = bundleNodeList.item(x).getAttributes().getNamedItem(StaticVariables.ID) - .getNodeValue(); - final Bundle bundle = getOrCreateBundle(bundleName); - bundle.getProducts().add(product); - product.getBundles().add(bundle); - } - } catch (ParserConfigurationException | SAXException | IOException e) { - LOGGER.log(System.Logger.Level.ERROR, "There was an error with reading the product xml file " + e); + LOGGER.log(System.Logger.Level.ERROR, "There was an error with reading the service xml file ", e); } } - + /** * Finds all files in a directory and all its subdirectories. Adds the path to * the files to List filePaths @@ -436,113 +563,83 @@ private void findFiles(final String name, final File file) { } } } - + /** - * Generates a list of {@link PackageObject} for a bundle. Packages can be - * exported or imported packages of a bundle + * Returns the bundle with the given identifying name. Will be created if the + * bundle does not exist yet. * - * @param bundle is the bundle which exports/imports the packages - * @param attributes is the String of packages - * @param key describes whether the packages are imported or exported - * @return + * @param name The unique name of the bundle. + * @return The bundle for the given name. */ - private void extractPackages(final Bundle bundle, final Attributes attributes) { - for (final String importOrExport : new String[] { StaticVariables.EXPORT_PACKAGE, - StaticVariables.IMPORT_PACKAGE }) { - - final String packageIds = attributes.getValue(importOrExport); - attributes.remove(new Attributes.Name(importOrExport)); - - if (null == packageIds) { - continue; - } - - for (final String b : packageIds.replaceAll("\\s", "").replaceAll("\"(.*?)\"", "").split(",")) { - final String packageName = b.split(";")[0]; - - if (importOrExport.equals(StaticVariables.EXPORT_PACKAGE)) { - final Package newExportedPackage = OSGiFactory.eINSTANCE.createPackage(); - newExportedPackage.setName(packageName); - newExportedPackage.setEcoreId(StaticVariables.PACKAGE_PREFIX + bundle.getEcoreId() + toAscii(packageName)); - newExportedPackage.getBundles().add(bundle); - bundle.getPackages().add(newExportedPackage); - project.getPackages().add(newExportedPackage); - } else { -// final Optional knownImportedPackage = packageDependencies// -// .stream()// -// .filter(elem -> elem.getName().equals(packageName))// -// .findFirst(); - final Optional ownProjectPackage = project.getPackages()// - .stream()// - .filter(elem -> elem.getName().equals(packageName))// - .findFirst(); - if (ownProjectPackage.isPresent()) { - ownProjectPackage.get().getConnectingPackageDependencyBundles().add(bundle); - bundle.getConnectedPackageDependencyPackages().add(ownProjectPackage.get()); -// } else if (knownImportedPackage.isPresent()) { -// bundle.getPackageDependency().add(knownImportedPackage.get()); - } else { - final Package newImportedPackage = OSGiFactory.eINSTANCE.createPackage(); - newImportedPackage.setName(packageName); - newImportedPackage.setEcoreId(StaticVariables.PACKAGE_PREFIX + toAscii(packageName)); - newImportedPackage.getConnectingPackageDependencyBundles().add(bundle); - bundle.getConnectedPackageDependencyPackages().add(newImportedPackage); -// packageDependencies.add(newImportedPackage); - project.getPackages().add(newImportedPackage); - } - } - } + private Bundle getOrCreateBundle(final String name) { + final Optional bundleAlreadyPresent = project.getBundles()// + .stream()// + .filter(elem -> elem.getEcoreId().equals(StaticVariables.BUNDLE_PREFIX + toAscii(name)))// + .findFirst(); + if (bundleAlreadyPresent.isPresent()) { + return bundleAlreadyPresent.get(); + } else { + final Bundle bundle = OSGiFactory.eINSTANCE.createBundle(); + bundle.setName(name); + bundle.setEcoreId(StaticVariables.BUNDLE_PREFIX + toAscii(name)); + bundle.setExternal(true); + project.getBundles().add(bundle); + return bundle; } } /** - * Returns the value of the attribute "key" out of an attribute list and removes - * it from the list. + * Returns the feature with the given identifying name. Will be created if the + * feature does not exist yet. * - * @param attributes the list of attributes - * @param key the attribute asked for - * @return the value of the attribute, if the attribute does not exist, return - * is null. + * @param name The unique name of the feature. + * @return The feature for the given name. */ - private static String getAndRemove(final Attributes attributes, final String key) { - final String value = attributes.getValue(key); - attributes.remove(new Attributes.Name(key)); - if (null == value) { - return StaticVariables.NOT_SET; + private Feature getOrCreateFeature(final String name) { + final Optional featureAlreadyPresent = project.getFeatures()// + .stream()// + .filter(elem -> elem.getEcoreId().equals(StaticVariables.FEATURE_PREFIX + toAscii(name)))// + .findFirst(); + if (featureAlreadyPresent.isPresent()) { + return featureAlreadyPresent.get(); + } else { + final Feature feature = OSGiFactory.eINSTANCE.createFeature(); + feature.setName(name); + feature.setEcoreId(StaticVariables.FEATURE_PREFIX + toAscii(name)); + feature.setExternal(true); + project.getFeatures().add(feature); + return feature; } - - return value.split(";")[0]; } /** - * Returns the value (comma separated list) of the attribute "key" out of an - * attribute list and removes it from the list. Splits the comma separated - * String of Strings into a list of Strings. + * Returns the service component with the given identifying name. Will be + * created if the component does not exist yet. * - * @param attributes - * @param key - * @return + * @param name The unique name of the service component. + * @return The service component for the given name. */ - private static List getList(final Attributes attributes, final String key) { - final String list = attributes.getValue(key); - attributes.remove(new Attributes.Name(key)); - final List result = new ArrayList(); - if (null == list) { - return result; - } - for (final String b : list.replaceAll("\"(.*?)\"", "") // remove all characters between '"' - .replaceAll("\\s", "") // remove all whitespaces - .split(",")) { - result.add(b.split(";")[0]); + private ServiceComponent getOrCreateServiceComponent(final String name) { + final Optional serviceComponentOptional = project.getServiceComponents()// + .stream()// + .filter(elem -> elem.getName().equals(StaticVariables.SERVICE_COMPONENT_PREFIX + toAscii(name)))// + .findFirst(); + if (serviceComponentOptional.isPresent()) { + return serviceComponentOptional.get(); + } else { + final ServiceComponent serviceComponent = OSGiFactory.eINSTANCE.createServiceComponent(); + serviceComponent.setName(name); + serviceComponent.setEcoreId(StaticVariables.SERVICE_COMPONENT_PREFIX + toAscii(name)); + project.getServiceComponents().add(serviceComponent); + return serviceComponent; } - return result; } /** * Converts the given name to an ACII string save for using in an Ecore ID. * German umlauts are converted to their long form counterparts (e.g., ä->ae) * and special characters not in the alphabet are replaced by underscores (_). - * + * * @param name The name to convert to an ASCII string * @return An ASCII-only version of the string. */ @@ -556,22 +653,61 @@ private String toAscii(String name) { mappings.put('ü', "ue"); mappings.put('ẞ', "Ss"); mappings.put('ß', "ss"); - + StringBuilder sb = new StringBuilder(); name.chars().forEachOrdered((int character) -> { // Replace all known mappings to readable allowable ID substrings if (mappings.containsKey((char) character)) { sb.append(mappings.get((char) character)); - // Keep all A-Z,a-z and .- the same. - } else if (character >= 'A' && character <= 'Z' || character >= 'a' && character <= 'z' || character == '.' || character == '-') { + // Keep all A-Z,a-z and .- the same. + } else if (character >= 'A' && character <= 'Z' || character >= 'a' && character <= 'z' || character == '.' + || character == '-') { sb.append((char) character); - // Replace all other characters by _ + // Replace all other characters by _ } else { sb.append('_'); } }); - + return sb.toString(); } + class LabeledEdge { + EdgeType type; + private T source; + private U target; + private V extraInfo; + + public LabeledEdge(EdgeType type, T source, U target, V extraInfo) { + super(); + this.type = type; + this.source = source; + this.target = target; + this.extraInfo = extraInfo; + } + + public EdgeType getType() { + return type; + } + + public T getSource() { + return source; + } + + public U getTarget() { + return target; + } + + public V getExtraInfo() { + return extraInfo; + } + + @Override + public String toString() { + return String.format("src: %s %ntarget: %s %nextraInfo: %s", source.toString(), target.toString(), + extraInfo.toString()); + } + + } + } diff --git a/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/mvn/MavenMojo.java b/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/mvn/MavenMojo.java new file mode 100644 index 0000000..4a5ff89 --- /dev/null +++ b/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/mvn/MavenMojo.java @@ -0,0 +1,66 @@ +// ****************************************************************************** +// +// Copyright (c) 2018-2025 by +// Scheidt & Bachmann System Technik GmbH, 24145 Kiel +// +// 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. +// +// SPDX-License-Identifier: EPL-2.0 +// +// ****************************************************************************** + +package de.cau.cs.kieler.spviz.osgi.generate.mvn; + +import java.io.File; +import java.lang.System.Logger; +import java.util.Optional; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; + +import de.cau.cs.kieler.spviz.osgi.generate.OsgiModelDataGenerator; + +/** + * + */ +@Mojo(name = "generate-spviz-osgi") +public class MavenMojo extends AbstractMojo { + static final Logger LOGGER = System.getLogger(MavenMojo.class.getName()); + @Parameter(name = "name", property = "SPVizName") + private String name; + + @Parameter(name = "sourceDir", property = "SPVizSource") + private String sourceDir; + + @Parameter(name = "targetDir", property = "SPVizTarget") + private String targetDir; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + System.out.println(String.format("%s -> %s -> %s", name, sourceDir, targetDir)); + File src = new File(sourceDir); + File trg = new File(targetDir); + if (!src.exists()) { + LOGGER.log(System.Logger.Level.ERROR, "Specify a valid source directory."); + throw new MojoFailureException("Invalid source directory"); + } + if (!trg.exists()) { + LOGGER.log(System.Logger.Level.ERROR, "Specify a valid target directory."); + throw new MojoFailureException("Invalid target directory"); + } + if (name == null || name.isBlank()) { + LOGGER.log(System.Logger.Level.ERROR, "Specify a valid name."); + throw new MojoFailureException("Invalid name"); + } + OsgiModelDataGenerator.generateData(sourceDir, name, Optional.of(targetDir)); + + LOGGER.log(System.Logger.Level.INFO, + "OSGi model generation has finished. The files can be found in " + targetDir); + } + +} diff --git a/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/util/EdgeType.java b/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/util/EdgeType.java new file mode 100644 index 0000000..d5e5abe --- /dev/null +++ b/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/util/EdgeType.java @@ -0,0 +1,18 @@ +// ****************************************************************************** +// +// Copyright (c) 2018-2025 by +// Scheidt & Bachmann System Technik GmbH, 24145 Kiel +// +// 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. +// +// SPDX-License-Identifier: EPL-2.0 +// +// ****************************************************************************** + +package de.cau.cs.kieler.spviz.osgi.generate.util; + +public enum EdgeType { + BUNDLE_DEPENDENCY, PACKAGE_DEPENDENCY +} diff --git a/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/util/FileUtil.java b/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/util/FileUtil.java new file mode 100644 index 0000000..cb419bc --- /dev/null +++ b/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/util/FileUtil.java @@ -0,0 +1,144 @@ +// ****************************************************************************** +// +// Copyright (c) 2018-2025 by +// Scheidt & Bachmann System Technik GmbH, 24145 Kiel +// +// 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. +// +// SPDX-License-Identifier: EPL-2.0 +// +// ****************************************************************************** + +package de.cau.cs.kieler.spviz.osgi.generate.util; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.DirectoryStream; +import java.nio.file.DirectoryStream.Filter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.stream.Collectors; + +/** + * Extractor for OSGI_INF folder from jar. + * + */ +public class FileUtil { + protected static final String BUNDLE_SYMBOLIC_NAME_TAG = "Bundle-SymbolicName"; + + /** + * Copys the xml files from the jar to the temporary folder. + * + * @param entry the jar entry with everything included in the jar file + * @param jarFile needed to create the input stream + * @param tempFilePath the path of the temporary file + */ + private static void copyXmlFileFromJarToDirectory(final JarEntry entry, final JarFile jarFile, + final Path tempFilePath) { + try (final InputStream stream = jarFile.getInputStream(entry);) { + final String fileName = entry.getName(); + final Path file = tempFilePath.resolve(fileName); + Files.createDirectories(file.getParent()); + Files.createFile(file); + Files.copy(stream, file, StandardCopyOption.REPLACE_EXISTING); + } catch (final IOException e) { + // NOOP if the file is unsuitable. + } + } + + /** + * Creates a OSGI-INF directory extracted from the jar file. + * + * @param rootPath the bundle path + * @param manifest the manifest for obtaining the name of the jar + * @throws IOException in case the rootPath is wrong + */ + // CHECKSTYLE IGNORE AbbreviationAsWordInNameCheck FOR NEXT 1 LINES - OSG is + // what it is called. + public static void createTemporaryOSGiInfDirectory(final Path rootPath, final Manifest manifest) + throws IOException { + final String symbolicName = getSymbolicName(manifest); + final List jarList = listFilesWithExtensionRecursively(rootPath, ".jar").stream() // + .map(Path::toFile) // + .filter(path -> path.getName().contains(symbolicName + "-")) // + .collect(Collectors.toList()); + for (final File fileWithJarEnding : jarList) { + extractXmlsFromJarIntoTempFolder(rootPath, fileWithJarEnding); + } + } + + private static final void deleteDirectory(final File file) { + if (file.isDirectory()) { + Arrays.stream(file.listFiles()) // + .forEach(FileUtil::deleteDirectory); + } + file.delete(); + } + + public static void deleteTempFolder(final File rootFile) { + final Path tempFolder = Paths.get(String.format("%s/%s", rootFile.getAbsolutePath(), "OSGI-INF")); + deleteDirectory(tempFolder.toFile()); + } + + /** + * Extracts the OSGI-INF directory from a jar, if it exists, and puts it into + * the temporary folder. + * + * @param rootPath root of the bundle + * @param fileWithJarEnding the jar which the OSGI-INF files are being extracted + * from + * @throws IOException in case there are file issues. + * + */ + private static void extractXmlsFromJarIntoTempFolder(final Path rootPath, final File fileWithJarEnding) + throws IOException { + final ArrayList osgiInfFiles = new ArrayList<>(); + try (JarFile jarFile = new JarFile(fileWithJarEnding.getAbsolutePath())) { + jarFile.entries().asIterator().forEachRemaining(entry -> { // + if (entry.getName().startsWith("OSGI-INF/") && entry.getName().endsWith(".xml")) { + osgiInfFiles.add(entry); + } + }); + osgiInfFiles.stream().forEach(entry -> copyXmlFileFromJarToDirectory(entry, jarFile, rootPath)); + } + } + + private static String getSymbolicName(final Manifest manifest) { + final String symbolicName = manifest.getMainAttributes().getValue(BUNDLE_SYMBOLIC_NAME_TAG).trim(); + return symbolicNameFromDeclaration(symbolicName); + } + + private static List listFilesWithExtensionRecursively(final Path path, final String fileExtension) + throws IOException { + final Filter filter = filterPath -> filterPath.toString().endsWith(fileExtension) + || filterPath.toFile().isDirectory(); + final List paths = new LinkedList<>(); + try (DirectoryStream stream = Files.newDirectoryStream(path, filter)) { + for (final Path entry : stream) { + if (Files.isDirectory(entry)) { + paths.addAll(listFilesWithExtensionRecursively(entry, fileExtension)); + } else { + paths.add(entry); + } + } + } + return paths; + } + + private static String symbolicNameFromDeclaration(final String symbolicNameDeclaration) { + return symbolicNameDeclaration.split(";")[0]; + } + +} diff --git a/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/ReadProjectFilesUtility.java b/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/util/ReadProjectFilesUtility.java similarity index 90% rename from osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/ReadProjectFilesUtility.java rename to osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/util/ReadProjectFilesUtility.java index 6e9fd99..363c89b 100644 --- a/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/ReadProjectFilesUtility.java +++ b/osgi/de.cau.cs.kieler.spviz.osgi.generate/src/de/cau/cs/kieler/spviz/osgi/generate/util/ReadProjectFilesUtility.java @@ -15,7 +15,7 @@ // // ****************************************************************************** -package de.cau.cs.kieler.spviz.osgi.generate; +package de.cau.cs.kieler.spviz.osgi.generate.util; import java.lang.System.Logger; import java.util.Comparator; @@ -38,7 +38,7 @@ public class ReadProjectFilesUtility { * This method takes an interface name, and returns the belonging bundle. * */ - static Bundle getBundleFromInterface(String interfaceName, OSGiProject project) { + public static Bundle getBundleFromInterface(String interfaceName, OSGiProject project) { return project.getBundles().stream()// .filter(bundle -> interfaceName.startsWith(bundle.getName()))// .max(Comparator.comparingInt(x -> x.getEcoreId().length())) From 48da3917162139a0567a8bdd2a26d44d48b7cae6 Mon Sep 17 00:00:00 2001 From: Jakob Haastert Date: Tue, 28 Jan 2025 11:16:20 +0100 Subject: [PATCH 2/3] moved model files and added back comments --- osgi/.gitignore | 3 ++- osgi/{de.cau.cs.kieler.spviz.osgi.generate => }/osgi.spviz | 2 ++ .../{de.cau.cs.kieler.spviz.osgi.generate => }/osgi.spvizmodel | 0 3 files changed, 4 insertions(+), 1 deletion(-) rename osgi/{de.cau.cs.kieler.spviz.osgi.generate => }/osgi.spviz (93%) rename osgi/{de.cau.cs.kieler.spviz.osgi.generate => }/osgi.spvizmodel (100%) diff --git a/osgi/.gitignore b/osgi/.gitignore index 85e312f..4695fd1 100644 --- a/osgi/.gitignore +++ b/osgi/.gitignore @@ -1,3 +1,4 @@ generated/ klighd-linux -spviz.cli.jar \ No newline at end of file +spviz.cli.jar +**/dependencies.txt \ No newline at end of file diff --git a/osgi/de.cau.cs.kieler.spviz.osgi.generate/osgi.spviz b/osgi/osgi.spviz similarity index 93% rename from osgi/de.cau.cs.kieler.spviz.osgi.generate/osgi.spviz rename to osgi/osgi.spviz index 80007a3..8c61f09 100644 --- a/osgi/de.cau.cs.kieler.spviz.osgi.generate/osgi.spviz +++ b/osgi/osgi.spviz @@ -31,11 +31,13 @@ SPViz OSGiViz { AllDependencies { show OSGi.Bundle connect OSGi.Bundle.Dependency + // connect OSGi.Bundle via OSGi.Bundle.PackageDependency connect OSGi.Bundle.PackageDependency } Products { show OSGi.Product + // connect OSGi.Bundle.Dependency via OSGi.Product>OSGi.Feature>OSGi.Bundle } Features { show OSGi.Feature diff --git a/osgi/de.cau.cs.kieler.spviz.osgi.generate/osgi.spvizmodel b/osgi/osgi.spvizmodel similarity index 100% rename from osgi/de.cau.cs.kieler.spviz.osgi.generate/osgi.spvizmodel rename to osgi/osgi.spvizmodel From 7ed8e3fdc84a5abc399f3c74b9f84149292b166c Mon Sep 17 00:00:00 2001 From: Jakob Haastert Date: Tue, 28 Jan 2025 11:18:57 +0100 Subject: [PATCH 3/3] removed dependencies.txt --- .../dependencies.txt | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 osgi/de.cau.cs.kieler.spviz.osgi.generate/dependencies.txt diff --git a/osgi/de.cau.cs.kieler.spviz.osgi.generate/dependencies.txt b/osgi/de.cau.cs.kieler.spviz.osgi.generate/dependencies.txt deleted file mode 100644 index 8f1bf1a..0000000 --- a/osgi/de.cau.cs.kieler.spviz.osgi.generate/dependencies.txt +++ /dev/null @@ -1,27 +0,0 @@ -de.cau.cs.kieler.spviz.osgi.generate:de.cau.cs.kieler.spviz.osgi.generate:maven-plugin:0.0.1-SNAPSHOT -+- org.apache.maven:maven-plugin-api:jar:3.9.6:compile -| +- org.apache.maven:maven-model:jar:3.9.6:compile -| +- org.apache.maven:maven-artifact:jar:3.9.6:compile -| | \- org.apache.commons:commons-lang3:jar:3.12.0:compile -| +- org.eclipse.sisu:org.eclipse.sisu.plexus:jar:0.9.0.M2:compile -| | +- javax.annotation:javax.annotation-api:jar:1.2:compile -| | +- org.eclipse.sisu:org.eclipse.sisu.inject:jar:0.9.0.M2:compile -| | \- org.codehaus.plexus:plexus-component-annotations:jar:2.1.0:compile -| +- org.codehaus.plexus:plexus-utils:jar:3.5.1:compile -| \- org.codehaus.plexus:plexus-classworlds:jar:2.7.0:compile -+- org.apache.maven.plugin-tools:maven-plugin-annotations:jar:3.9.0:compile -+- org.eclipse.emf:org.eclipse.emf.ecore.xmi:jar:2.16.0:compile -| \- org.eclipse.emf:org.eclipse.emf.ecore:jar:2.38.0:compile (version selected from constraint [2.18.0,3.0.0)) -| \- org.eclipse.emf:org.eclipse.emf.common:jar:2.40.0:compile (version selected from constraint [2.40.0,3.0.0)) -+- de.cau.cs.kieler.spviz.osgi:de.cau.cs.kieler.spviz.osgi.model:jar:0.1.0-SNAPSHOT:compile -| +- com.google.guava:guava:jar:32.1.3-jre:compile -| | +- com.google.guava:listenablefuture:jar:9999.0-empty-to-avoid-conflict-with-guava:compile -| | +- com.google.code.findbugs:jsr305:jar:3.0.2:compile -| | +- org.checkerframework:checker-qual:jar:3.37.0:compile -| | +- com.google.errorprone:error_prone_annotations:jar:2.21.1:compile -| | \- com.google.j2objc:j2objc-annotations:jar:2.8:compile -| +- com.google.guava:failureaccess:jar:1.0.2:compile -| \- org.osgi:org.osgi.service.prefs:jar:1.1.2:compile -| \- org.osgi:osgi.annotation:jar:8.0.1:compile -+- info.picocli:picocli:jar:4.6.3:compile -\- commons-io:commons-io:jar:2.11.0:compile