/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.aries.versioning.check; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.lang.String; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.apache.aries.util.filesystem.FileSystem; import org.apache.aries.util.filesystem.IDirectory; import org.apache.aries.util.filesystem.IFile; import org.apache.aries.util.io.IOUtils; import org.apache.aries.util.manifest.BundleManifest; import org.apache.aries.versioning.utils.SemanticVersioningUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SemanticVersioningChecker { private static final Logger _logger = LoggerFactory.getLogger(SemanticVersioningChecker.class); private URLClassLoader newJarsLoader; private URLClassLoader oldJarsLoader; private static final String xmlHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"; /** * This method is to scan the current location against base and process each individual jars * in the current location against the jar with the same symbolic names in the base and produce a xml report specified by versioningReport. * * @param base baseline jars * @param current current version of jars * @param versioningReport the validation reports */ public static void checkSemanticVersioning(URL base, URL current, File versioningReport) { //For each jar under the current location, open the jar and then use ASM to //work out whether there are binary incompatible changes against the base location. try { File baseDir = new File(base.toURI()); File currentDir = new File(current.toExternalForm()); if (baseDir.exists() && currentDir.exists()) { new SemanticVersioningChecker().performVersioningCheck(FileSystem.getFSRoot(baseDir), FileSystem.getFSRoot(currentDir), versioningReport); } else { _logger.debug("No bundles found to process."); } } catch (URISyntaxException use) { _logger.error(use.getMessage()); } } // for each jar, open its manifest and found the packages private void performVersioningCheck(IDirectory baseDir, IDirectory currentDir, File versionStatusFile) { FileWriter versionStatusFileWriter = null; try { versionStatusFileWriter = new FileWriter(versionStatusFile, false); Map<String, BundleInfo> baseBundles; Map<String, BundleInfo> currentBundles; //scan each individual bundle and find the corresponding bundle in the baseline and verify the version changes currentBundles = getBundles(currentDir); baseBundles = getBundles(baseDir); URL[] newJarURLs = getListURLs(currentBundles.values()).toArray(new URL[0]); newJarsLoader = new URLClassLoader(newJarURLs); URL[] oldJarURLs = getListURLs(baseBundles.values()).toArray(new URL[0]); oldJarsLoader = new URLClassLoader(oldJarURLs); //Write the xml header writeRecordToWriter(versionStatusFileWriter, xmlHeader + "\r\n"); // write the comparison base and current level into the file writeRecordToWriter(versionStatusFileWriter, "<semanticVersioning currentDir= \"" + currentDir + "\" baseDir = \"" + baseDir + "\">"); for (Map.Entry<String, BundleInfo> entry : currentBundles.entrySet()) { String bundleSymbolicName = entry.getKey(); String bundleElement = null; boolean bundleVersionCorrect = true; // find the same bundle in the base and check whether all the versions are correct BundleInfo currentBundle = entry.getValue(); BundleInfo baseBundle = baseBundles.get(bundleSymbolicName); StringBuilder pkgElements = new StringBuilder(); if (baseBundle == null) { _logger.debug("The bundle " + bundleSymbolicName + " has no counterpart in the base. The semantic version validation does not apply to this bundle."); } else { BundleCompatibility bundleCompatibility = new BundleCompatibility(bundleSymbolicName, currentBundle, baseBundle, oldJarsLoader, newJarsLoader).invoke(); bundleVersionCorrect = bundleCompatibility.isBundleVersionCorrect(); bundleElement = bundleCompatibility.getBundleElement(); pkgElements = bundleCompatibility.getPkgElements(); } // Need to write bundle element and then package elements if ((!!!bundleVersionCorrect) || ((pkgElements.length() > 0))) { writeRecordToWriter(versionStatusFileWriter, bundleElement); writeRecordToWriter(versionStatusFileWriter, pkgElements.toString()); writeRecordToWriter(versionStatusFileWriter, "</bundle>"); } } writeRecordToWriter(versionStatusFileWriter, "</semanticVersioning>"); } catch (IOException ioe) { ioe.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { IOUtils.close(versionStatusFileWriter); } return; } private void writeRecordToWriter(FileWriter fileWriter, String stringToWrite) throws IOException { if (fileWriter != null) { fileWriter.append(stringToWrite); fileWriter.append("\r\n"); } } private Map<String, BundleInfo> getBundles(IDirectory ds) { Map<String, BundleInfo> bundles = new HashMap<String, BundleInfo>(); List<IFile> includedFiles = ds.listAllFiles(); for (IFile ifile : includedFiles) { if (ifile.getName().endsWith(SemanticVersioningUtils.jarExt)) { // scan its manifest try { BundleManifest manifest = BundleManifest.fromBundle(ifile.open()); // find the bundle symbolic name, store them in a map with bundle symbolic name as key, bundleInfo as value if (manifest.getSymbolicName() != null) { bundles.put(manifest.getSymbolicName(), new BundleInfo(manifest, new File(ifile.toURL().getPath()))); } } catch (MalformedURLException mue) { _logger.debug("Exception thrown when processing" + ifile.getName(), mue); } catch (IOException ioe) { _logger.debug("Exception thrown when processing" + ifile.getName(), ioe); } } } return bundles; } private Collection<URL> getListURLs(Collection<BundleInfo> bundles) { Collection<URL> urls = new HashSet<URL>(); try { for (BundleInfo bundle : bundles) { URL url = bundle.getBundle().toURI().toURL(); urls.add(url); } } catch (MalformedURLException e) { _logger.debug(e.getMessage()); } return urls; } }