blob: 9b5d8b2f35e13d09fa34768015eaa861c2de43d3 [file] [log] [blame]
/*
* 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.netbeans.core.startup;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.netbeans.Events;
import org.netbeans.InvalidException;
import org.netbeans.Module;
import org.netbeans.ModuleInstaller;
import org.netbeans.ModuleManager;
import org.openide.modules.Dependency;
/**
* Utility class permitting you to verify that a set of modules could be enabled together.
* Currently used from <code>org.netbeans.core.validation.ValidateUpdateCenterTest</code>.
* @author Jesse Glick
*/
public class ConsistencyVerifier {
private ConsistencyVerifier() {}
/**
* Find all expected installation problems for a set of modules.
* Standard OS and module format tokens are provided, but all other dependencies
* must be accessible from the set of modules supplied.
* @param modules a set of module manifests to test together
* @return a map from module code name bases, to sets of problems, expressed in an unspecified but readable format
* @throws IllegalArgumentException if the set of modules is illegal (e.g. contains duplicates)
*/
public static SortedMap<String,SortedSet<String>> findInconsistencies(Set<Manifest> modules) throws IllegalArgumentException {
return findInconsistencies(modules, null);
}
/**
* Find all expected installation problems for a set of modules.
* Standard OS and module format tokens are provided, but all other dependencies
* must be accessible from the set of modules supplied.
* <p>The manifests may contain the pseudoattributes <code>autoload</code> and <code>eager</code>
* which if set to <code>true</code> will mark the corresponding modules autoload or eager (resp.).
* It is considered an error if some nondeprecated autoload modules would not be enabled unless explicitly whitelisted.
* @param modules a set of module manifests to test together
* @param permittedDisabledAutoloads if null, do not check autoload enablement;
* otherwise (not null but possibly empty) permit the listed modules (as CNBs) to be disabled
* @return a map from module code name bases, to sets of problems, expressed in an unspecified but readable format
* @throws IllegalArgumentException if the set of modules is illegal (e.g. contains duplicates)
*/
public static SortedMap<String,SortedSet<String>> findInconsistencies(
Set<Manifest> modules, Set<String> permittedDisabledAutoloads) throws IllegalArgumentException {
return findInconsistencies(modules, permittedDisabledAutoloads, true);
}
/* accessible to test */ static SortedMap<String,SortedSet<String>> findInconsistencies(
Set<Manifest> modules, Set<String> permittedDisabledAutoloads, boolean formatted) throws IllegalArgumentException {
ModuleManager mgr = new ModuleManager(new DummyInstaller(), new DummyEvents());
mgr.mutexPrivileged().enterWriteAccess();
Manifest dummy = new Manifest();
dummy.getMainAttributes().putValue("OpenIDE-Module", "__dummy__"); // NOI18N
dummy.getMainAttributes().putValue("OpenIDE-Module-Provides",
"org.openide.modules.ModuleFormat1, " + // NOI18N
"org.openide.modules.ModuleFormat2, " + // NOI18N
"org.openide.modules.jre.JavaFX, " + // NOI18N
"org.openide.modules.os.Unix, " + // NOI18N
"org.openide.modules.os.PlainUnix, " + // NOI18N
"org.openide.modules.os.Windows, " + // NOI18N
"org.openide.modules.os.MacOSX, " + // NOI18N
"org.openide.modules.os.Linux, " + // NOI18N
"org.openide.modules.os.Solaris, " + // NOI18N
"org.openide.modules.os.OS2"); // NOI18N
dummy.getMainAttributes().putValue("OpenIDE-Module-Public-Packages", "-"); // NOI18N
try {
mgr.createFixed(dummy, null, ClassLoader.getSystemClassLoader());
} catch (Exception x) {
throw new AssertionError(x);
}
Set<Module> mods = new HashSet<Module>();
Set<Module> regularMods = new HashSet<Module>();
for (Manifest m : modules) {
final Attributes man = m.getMainAttributes();
try {
man.putValue("OpenIDE-Module-Public-Packages", "-"); // NOI18N
man.remove(new Attributes.Name("OpenIDE-Module-Friends")); // NOI18N
man.remove(new Attributes.Name("OpenIDE-Module-Localizing-Bundle")); // NOI18N
String bsn = man.getValue("Bundle-SymbolicName"); // NOI18N
String ver = man.getValue("Bundle-Version"); // NOI18N
if (bsn != null && ver != null) {
bsn = bsn.replaceAll(";.*", "");
if (man.getValue("OpenIDE-Module") == null) { // NOI18N
man.putValue("OpenIDE-Module", bsn); // NOI18N
}
if (man.getValue("OpenIDE-Module-Specification-Version") == null) { // NOI18N
Matcher match = Pattern.compile("[0-9]*(\\.[0-9]*)?(\\.[0-9]*)?").matcher(ver);
if (match.find()) {
ver = match.group();
}
man.putValue("OpenIDE-Module-Specification-Version", ver.replace("(.*)", "")); // NOI18N
}
}
boolean autoload = "true".equals(man.getValue("autoload"));
boolean eager = "true".equals(man.getValue("eager"));
if (autoload) {
// discard dependency on JDK: will allow other modules, dependent on these autoloads, to enable
man.remove(new Attributes.Name("OpenIDE-Module-Java-Dependencies"));
}
Module mod = mgr.createFixed(m, null, ClassLoader.getSystemClassLoader(), autoload, eager);
mods.add(mod);
if (!autoload && !eager) {
regularMods.add(mod);
}
} catch (Exception x) {
throw new IllegalArgumentException("Error parsing " + man.entrySet() + ": " + x, x);
}
}
SortedMap<String,SortedSet<String>> problems = new TreeMap<String,SortedSet<String>>();
List<Module> regularModsEnabled = mgr.simulateEnable(regularMods);
for (Module m : mods) {
String cnb = m.getCodeNameBase();
Set<Object> probs = m.getProblems();
if (probs.isEmpty()) {
if (permittedDisabledAutoloads != null && !permittedDisabledAutoloads.contains(cnb) && m.isAutoload() &&
!regularModsEnabled.contains(m) && !"true".equals(m.getAttribute("OpenIDE-Module-Deprecated"))) {
problems.put(cnb, new TreeSet<String>(Collections.singleton("module is autoload but would not be enabled")));
}
continue;
}
SortedSet<String> probnames = new TreeSet<String>();
for (Object prob : probs) {
if (prob instanceof Dependency) {
Dependency d = (Dependency) prob;
if (Dependency.TYPE_JAVA == d.getType() && (m.isEager() || m.isAutoload())) {
continue;
}
}
String description;
if (formatted) {
description = NbProblemDisplayer.messageForProblem(m, prob, false);
} else {
description = prob.toString();
}
probnames.add(description);
}
if (!probnames.isEmpty()) {
problems.put(cnb, probnames);
}
}
return problems;
}
private static final class DummyInstaller extends ModuleInstaller {
public void prepare(Module m) throws InvalidException {
throw new AssertionError();
}
public void dispose(Module m) {
throw new AssertionError();
}
public void load(List<Module> modules) {
throw new AssertionError();
}
public void unload(List<Module> modules) {
throw new AssertionError();
}
public boolean closing(List<Module> modules) {
throw new AssertionError();
}
public void close(List<Module> modules) {
throw new AssertionError();
}
}
private static final class DummyEvents extends Events {
protected void logged(String message, Object[] args) {}
}
}