You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by si...@apache.org on 2018/09/15 08:37:28 UTC

[sling-org-apache-sling-feature-analyser] branch SLING-7925 created (now 38eb881)

This is an automated email from the ASF dual-hosted git repository.

simonetripodi pushed a change to branch SLING-7925
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-analyser.git.


      at 38eb881  SLING-7925 - Donate new AnalyzerTask which is able to validate APIs/Regions

This branch includes the following new commits:

     new 38eb881  SLING-7925 - Donate new AnalyzerTask which is able to validate APIs/Regions

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[sling-org-apache-sling-feature-analyser] 01/01: SLING-7925 - Donate new AnalyzerTask which is able to validate APIs/Regions

Posted by si...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

simonetripodi pushed a commit to branch SLING-7925
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-analyser.git

commit 38eb881e5c684dad724102ce0f6730e6da5b063a
Author: Simo Tripodi <st...@adobe.com>
AuthorDate: Sat Sep 15 10:35:59 2018 +0200

    SLING-7925 - Donate new AnalyzerTask which is able to validate
    APIs/Regions
    
    initial checkin
---
 .../analyser/task/impl/CheckApiRegions.java        | 222 +++++++++++++++++++++
 .../scanner/impl/ApiRegionsExtensionScanner.java   |  56 ++++++
 ...apache.sling.feature.analyser.task.AnalyserTask |   2 +-
 ...ache.sling.feature.scanner.spi.ExtensionScanner |   2 +-
 4 files changed, 280 insertions(+), 2 deletions(-)

diff --git a/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckApiRegions.java b/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckApiRegions.java
new file mode 100644
index 0000000..6ef2b89
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckApiRegions.java
@@ -0,0 +1,222 @@
+/*
+ * 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.sling.feature.analyser.task.impl;
+
+import java.io.StringReader;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Formatter;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.json.Json;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParser.Event;
+
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.Extensions;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.analyser.task.AnalyserTask;
+import org.apache.sling.feature.analyser.task.AnalyserTaskContext;
+import org.apache.sling.feature.scanner.BundleDescriptor;
+import org.apache.sling.feature.scanner.FeatureDescriptor;
+import org.apache.sling.feature.scanner.PackageInfo;
+
+public class CheckApiRegions implements AnalyserTask {
+
+    private static final String API_REGIONS_KEY = "api-regions";
+
+    private static final String NAME_KEY = "name";
+
+    private static final String EXPORTS_KEY = "exports";
+
+    @Override
+    public String getId() {
+        return API_REGIONS_KEY;
+    }
+
+    @Override
+    public String getName() {
+        return "Api Regions analyser task";
+    }
+
+    @Override
+    public void execute(AnalyserTaskContext ctx) throws Exception {
+        Feature feature = ctx.getFeature();
+
+        // extract and check the api-regions
+
+        Extensions extensions = feature.getExtensions();
+        Extension apiRegionsExtension = extensions.getByName(API_REGIONS_KEY);
+        if (apiRegionsExtension == null) {
+            // no need to be analyzed
+            return;
+        }
+
+        String jsonRepresentation = apiRegionsExtension.getJSON();
+        if (jsonRepresentation == null || jsonRepresentation.isEmpty()) {
+            // no need to be analyzed
+            return;
+        }
+
+        // read the api-regions and create a Sieve data structure for checks
+
+        ApiRegions apiRegions = fromJson(jsonRepresentation);
+
+        // then, for each bundle, get the Export-Package and process the packages
+
+        FeatureDescriptor featureDescriptor = ctx.getFeatureDescriptor();
+        for (BundleDescriptor bundleDescriptor : featureDescriptor.getBundleDescriptors()) {
+            for (PackageInfo packageInfo : bundleDescriptor.getExportedPackages()) {
+                String exportedPackage = packageInfo.getName();
+                // use the Sieve technique: remove bundle exported packages from the api-regions
+                apiRegions.remove(exportedPackage);
+            }
+        }
+
+        // final evaluation: if the Sieve is not empty, not all declared packages are exported by bundles of the same feature
+        if (!apiRegions.isEmpty()) {
+            // track a single error for each region
+            for (String region : apiRegions.getRegions()) {
+                Set<String> apis = apiRegions.getApis(region);
+                if (!apis.isEmpty()) {
+                    Formatter formatter = new Formatter();
+                    formatter.format("Region '%s' defined in feature '%s' declares %s package%s which %s not exported by any bundle:%n",
+                                     region,
+                                     feature.getId(),
+                                     apis.size(),
+                                     getExtension(apis, "", "s"),
+                                     getExtension(apis, "is", "are"));
+                    apis.forEach(api -> formatter.format(" * %s%n", api));
+
+                    ctx.reportError(formatter.toString());
+
+                    formatter.close();
+                }
+            }
+        }
+    }
+
+    // utility methods
+
+    private static <T> String getExtension(Collection<T> collection, String singular, String plural) {
+        return collection.size() > 1 ? plural : singular;
+    }
+
+    private static ApiRegions fromJson(String jsonRepresentation) {
+        ApiRegions apiRegions = new ApiRegions();
+
+        // pointers
+        Event event;
+        String region = null;
+        Collection<String> apis = null;
+
+        JsonParser parser = Json.createParser(new StringReader(jsonRepresentation));
+        while (parser.hasNext()) {
+            event = parser.next();
+            if (Event.KEY_NAME == event) {
+                switch (parser.getString()) {
+                    case NAME_KEY:
+                        parser.next();
+                        region = parser.getString();
+                        break;
+
+                    case EXPORTS_KEY:
+                        apis = new LinkedList<>();
+
+                        // start array
+                        parser.next();
+
+                        while (parser.hasNext() && Event.VALUE_STRING == parser.next()) {
+                            String api = parser.getString();
+                            // skip comments
+                            if ('#' != api.charAt(0)) {
+                                apis.add(api);
+                            }
+                        }
+
+                        break;
+
+                    default:
+                        break;
+                }
+            } else if (Event.END_OBJECT == event) {
+                if (region != null && apis != null) {
+                    apiRegions.add(region, apis);
+                }
+
+                region = null;
+                apis = null;
+            }
+        }
+
+        return apiRegions;
+    }
+
+    // the Sieve data structure to check exported packages
+
+    private static final class ApiRegions {
+
+        // class members
+
+        private final Map<String, Set<String>> apis = new TreeMap<>();
+
+        // ctor
+
+        protected ApiRegions() {
+            // it should not be directly instantiated outside this package
+        }
+
+        // methods
+
+        public void add(String region, Collection<String> exportedApis) {
+            apis.computeIfAbsent(region, k -> new TreeSet<>()).addAll(exportedApis);
+        }
+
+        public Iterable<String> getRegions() {
+            return apis.keySet();
+        }
+
+        public Set<String> getApis(String region) {
+            return apis.computeIfAbsent(region, k -> Collections.emptySet());
+        }
+
+        public void remove(String packageName) {
+            apis.values().forEach(apis -> apis.remove(packageName));
+        }
+
+        public boolean isEmpty() {
+            for (Set<String> packages : apis.values()) {
+                if (!packages.isEmpty()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return apis.toString().replace(',', '\n');
+        }
+
+    }
+
+
+}
diff --git a/src/main/java/org/apache/sling/feature/scanner/impl/ApiRegionsExtensionScanner.java b/src/main/java/org/apache/sling/feature/scanner/impl/ApiRegionsExtensionScanner.java
new file mode 100644
index 0000000..0aaa9b5
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/scanner/impl/ApiRegionsExtensionScanner.java
@@ -0,0 +1,56 @@
+/*
+ * 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.sling.feature.scanner.impl;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.io.ArtifactManager;
+import org.apache.sling.feature.scanner.BundleDescriptor;
+import org.apache.sling.feature.scanner.ContainerDescriptor;
+import org.apache.sling.feature.scanner.FeatureDescriptor;
+import org.apache.sling.feature.scanner.spi.ExtensionScanner;
+
+public class ApiRegionsExtensionScanner implements ExtensionScanner {
+
+    @Override
+    public String getId() {
+        return "api-regions";
+    }
+
+    @Override
+    public String getName() {
+        return "Api Regions extention scanner";
+    }
+
+    @Override
+    public ContainerDescriptor scan(Feature feature, Extension extension, ArtifactManager manager) throws IOException {
+        FeatureDescriptor featureDescriptor = new FeatureDescriptorImpl(feature);
+
+        for (Artifact artifact : feature.getBundles()) {
+            File file = manager.getArtifactHandler(artifact.getId().toMvnUrl()).getFile();
+            BundleDescriptor bundleDescriptor = new BundleDescriptorImpl(artifact, file, artifact.getStartOrder());
+            featureDescriptor.getBundleDescriptors().add(bundleDescriptor);
+        }
+
+        return featureDescriptor;
+    }
+
+}
diff --git a/src/main/resources/META-INF/services/org.apache.sling.feature.analyser.task.AnalyserTask b/src/main/resources/META-INF/services/org.apache.sling.feature.analyser.task.AnalyserTask
index dabd8fb..7f58e8d 100644
--- a/src/main/resources/META-INF/services/org.apache.sling.feature.analyser.task.AnalyserTask
+++ b/src/main/resources/META-INF/services/org.apache.sling.feature.analyser.task.AnalyserTask
@@ -2,4 +2,4 @@ org.apache.sling.feature.analyser.task.impl.CheckBundleExportsImports
 org.apache.sling.feature.analyser.task.impl.CheckBundlesForInitialContent
 org.apache.sling.feature.analyser.task.impl.CheckBundlesForResources
 org.apache.sling.feature.analyser.task.impl.CheckRequirementsCapabilities
-
+org.apache.sling.feature.analyser.task.impl.CheckApiRegions
diff --git a/src/main/resources/META-INF/services/org.apache.sling.feature.scanner.spi.ExtensionScanner b/src/main/resources/META-INF/services/org.apache.sling.feature.scanner.spi.ExtensionScanner
index 643c7cf..528834d 100644
--- a/src/main/resources/META-INF/services/org.apache.sling.feature.scanner.spi.ExtensionScanner
+++ b/src/main/resources/META-INF/services/org.apache.sling.feature.scanner.spi.ExtensionScanner
@@ -1,3 +1,3 @@
 org.apache.sling.feature.scanner.impl.ContentPackagesExtensionScanner
 org.apache.sling.feature.scanner.impl.RepoInitScanner
-
+org.apache.sling.feature.scanner.impl.ApiRegionsExtensionScanner