/******************************************************************************* * Copyright (c) 2009, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Ericsson AB - Ongoing development * Red Hat Inc. - Bug 460967 *******************************************************************************/ package org.eclipse.equinox.internal.p2.tests.verifier; import java.io.*; import java.net.URI; import java.util.*; import java.util.Map.Entry; import org.eclipse.core.runtime.*; import org.eclipse.equinox.app.IApplication; import org.eclipse.equinox.app.IApplicationContext; import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper; import org.eclipse.equinox.internal.p2.ui.sdk.scheduler.migration.MigrationWizard; import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.equinox.p2.engine.IProfile; import org.eclipse.equinox.p2.engine.IProfileRegistry; import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.query.IQueryResult; import org.eclipse.equinox.p2.query.QueryUtil; import org.eclipse.osgi.report.resolution.ResolutionReport; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Display; import org.osgi.framework.*; import org.osgi.framework.hooks.resolver.ResolverHook; import org.osgi.framework.hooks.resolver.ResolverHookFactory; import org.osgi.framework.namespace.IdentityNamespace; import org.osgi.framework.wiring.*; import org.osgi.resource.*; /** * Application which verifies an install. * * @since 1.0 */ public class VerifierApplication implements IApplication { private static final File DEFAULT_PROPERTIES_FILE = new File("verifier.properties"); //$NON-NLS-1$ private static final String ARG_PROPERTIES = "-verifier.properties"; //$NON-NLS-1$ private IProvisioningAgent agent; private Properties properties = null; private List ignoreResolved = null; /* * Create and return an error status with the given message. */ private static IStatus createError(String message) { return new Status(IStatus.ERROR, Activator.PLUGIN_ID, message); } /* (non-Javadoc) * @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.IApplicationContext) */ public Object start(IApplicationContext context) throws Exception { String[] args = (String[]) context.getArguments().get(IApplicationContext.APPLICATION_ARGS); processArguments(args); agent = ServiceHelper.getService(Activator.getBundleContext(), IProvisioningAgent.class); IStatus result = verify(); if (!result.isOK()) { // PrintWriter out = new PrintWriter(new FileWriter(new File("c:/tmp/dropins-debug.txt"))); PrintWriter out = new PrintWriter(new OutputStreamWriter(System.err)); out.println("Error from dropin verifier application: " + result.getMessage()); //$NON-NLS-1$ Throwable t = result.getException(); if (t != null) t.printStackTrace(out); out.close(); LogHelper.log(result); } return result.isOK() ? IApplication.EXIT_OK : Integer.valueOf(13); } /* * Go through the command-line args and pull out interesting ones * for later consumption. */ private void processArguments(String[] args) { if (args == null) return; for (int i = 1; i < args.length; i++) { if (ARG_PROPERTIES.equals(args[i - 1])) { String filename = args[i]; if (filename.startsWith("-")) //$NON-NLS-1$ continue; try { properties = readProperties(new File(filename)); } catch (IOException e) { // TODO e.printStackTrace(); // fall through to load default } continue; } } // problems loading properties file or none specified so look for a default if (properties == null) { try { if (DEFAULT_PROPERTIES_FILE.exists()) properties = readProperties(DEFAULT_PROPERTIES_FILE); } catch (IOException e) { // TODO e.printStackTrace(); } } if (properties == null) properties = new Properties(); } /* * Read and return a properties file at the given location. */ private Properties readProperties(File file) throws IOException { Properties result = new Properties(); InputStream input = null; try { input = new BufferedInputStream(new FileInputStream(file)); result.load(input); return result; } finally { if (input != null) try { input.close(); } catch (IOException e) { // ignore } } } /* (non-Javadoc) * @see org.eclipse.equinox.app.IApplication#stop() */ public void stop() { // nothing to do } /* * Return a boolean value indicating whether or not the bundle with the given symbolic name * should be considered when looking at bundles which are not resolved in the system. * TODO the call to this method was removed. we should add it back */ protected boolean shouldCheckResolved(String bundle) { if (ignoreResolved == null) { ignoreResolved = new ArrayList(); String list = properties.getProperty("ignore.unresolved"); if (list == null) return true; for (StringTokenizer tokenizer = new StringTokenizer(list, ","); tokenizer.hasMoreTokens();) ignoreResolved.add(tokenizer.nextToken().trim()); } for (Iterator iter = ignoreResolved.iterator(); iter.hasNext();) { if (bundle.equals(iter.next())) return false; } return true; } /* * Check to ensure all of the bundles in the system are resolved. * * Copied and modified from EclipseStarter#logUnresolvedBundles. * This method prints out all the reasons while asking the resolver directly * will only print out the first reason. */ private IStatus checkResolved() { List allProblems = new ArrayList(); List<Bundle> unresolved = new ArrayList(); for (Bundle b : Activator.getBundleContext().getBundles()) { BundleRevision revision = b.adapt(BundleRevision.class); if (revision != null && revision.getWiring() == null) { unresolved.add(b); } } ResolutionReport report = getResolutionReport(unresolved); Map<Resource, List<ResolutionReport.Entry>> entries = report.getEntries(); Collection<Resource> unresolvedResources = new HashSet<Resource>(entries.keySet()); Collection<Resource> leafResources = new HashSet<Resource>(); for (Map.Entry<Resource, List<ResolutionReport.Entry>> resourceEntry : entries.entrySet()) { for (ResolutionReport.Entry reportEntry : resourceEntry.getValue()) { if (!reportEntry.getType().equals(ResolutionReport.Entry.Type.UNRESOLVED_PROVIDER)) { leafResources.add(resourceEntry.getKey()); unresolvedResources.remove(resourceEntry.getKey()); } } } // first lets look for missing leaf constraints (bug 114120) for (Resource leafResource : leafResources) { BundleRevision revision = (BundleRevision) leafResource; String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_ERROR_BUNDLE_NOT_RESOLVED, revision.getBundle().getLocation()) + '\n'; message += report.getResolutionReportMessage(leafResource); allProblems.add(createError(message)); } // now report all others for (Resource unresolvedResource : unresolvedResources) { BundleRevision revision = (BundleRevision) unresolvedResource; String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_ERROR_BUNDLE_NOT_RESOLVED, revision.getBundle().getLocation()) + '\n'; message += report.getResolutionReportMessage(unresolvedResource); allProblems.add(createError(message)); } MultiStatus result = new MultiStatus(Activator.PLUGIN_ID, IStatus.OK, "Problems checking resolved bundles.", null); //$NON-NLS-1$ for (Iterator iter = allProblems.iterator(); iter.hasNext();) result.add((IStatus) iter.next()); return result; } private static ResolutionReport getResolutionReport(Collection<Bundle> bundles) { BundleContext context = Activator.getBundleContext(); DiagReportListener reportListener = new DiagReportListener(bundles); ServiceRegistration<ResolverHookFactory> hookReg = context.registerService(ResolverHookFactory.class, reportListener, null); try { Bundle systemBundle = context.getBundle(Constants.SYSTEM_BUNDLE_LOCATION); FrameworkWiring frameworkWiring = systemBundle.adapt(FrameworkWiring.class); frameworkWiring.resolveBundles(bundles); return reportListener.getReport(); } finally { hookReg.unregister(); } } private static class DiagReportListener implements ResolverHookFactory { private final Collection<BundleRevision> targetTriggers = new ArrayList<BundleRevision>(); public DiagReportListener(Collection<Bundle> bundles) { for (Bundle bundle : bundles) { BundleRevision revision = bundle.adapt(BundleRevision.class); if (revision != null && revision.getWiring() == null) { targetTriggers.add(revision); } } } volatile ResolutionReport report = null; class DiagResolverHook implements ResolverHook, ResolutionReport.Listener { public void handleResolutionReport(ResolutionReport handleReport) { DiagReportListener.this.report = handleReport; } public void filterResolvable(Collection<BundleRevision> candidates) { // nothing } public void filterSingletonCollisions(BundleCapability singleton, Collection<BundleCapability> collisionCandidates) { // nothing } public void filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates) { // nothing } public void end() { // nothing } } public ResolverHook begin(Collection<BundleRevision> triggers) { if (triggers.containsAll(targetTriggers)) { return new DiagResolverHook(); } return null; } ResolutionReport getReport() { return report; } } /* * Ensure we have a profile registry and can access the SELF profile. */ private IStatus checkProfileRegistry() { IProfileRegistry registry = (IProfileRegistry) agent.getService(IProfileRegistry.SERVICE_NAME); if (registry == null) return createError("Profile registry service not available."); //$NON-NLS-1$ IProfile profile = registry.getProfile(IProfileRegistry.SELF); if (profile == null) return createError("SELF profile not available in profile registry."); //$NON-NLS-1$ if (properties.get("checkPresenceOfVerifier") != null && !Boolean.FALSE.toString().equals(properties.get("checkPresenceOfVerifier"))) { IQueryResult results = profile.query(QueryUtil.createIUQuery(Activator.PLUGIN_ID), null); if (results.isEmpty()) return createError(NLS.bind("IU for {0} not found in SELF profile.", Activator.PLUGIN_ID)); //$NON-NLS-1$ } return Status.OK_STATUS; } /* * Perform all of the verification checks. */ public IStatus verify() { String message = "Problems occurred during verification."; //$NON-NLS-1$ MultiStatus result = new MultiStatus(Activator.PLUGIN_ID, IStatus.OK, message, null); // ensure all the bundles are resolved IStatus temp = checkResolved(); if (!temp.isOK()) result.merge(temp); // ensure we have a profile registry temp = checkProfileRegistry(); if (!temp.isOK()) result.merge(temp); temp = hasProfileFlag(); if (!temp.isOK()) result.merge(temp); temp = checkAbsenceOfBundles(); if (!temp.isOK()) result.merge(temp); temp = checkPresenceOfBundles(); if (!temp.isOK()) result.merge(temp); temp = checkSystemProperties(); if (!temp.isOK()) result.merge(temp); temp = checkMigrationWizard(); if (!temp.isOK()) result.merge(temp); assumeMigrated(); handleWizardCancellation(); return result; } private void handleWizardCancellation() { if (properties.getProperty("checkMigration.cancelAnswer") == null) return; new Display(); IProfileRegistry reg = (IProfileRegistry) agent.getService(IProfileRegistry.SERVICE_NAME); IProfile profile = reg.getProfile(IProfileRegistry.SELF); MigrationWizard wizardPage = new MigrationWizard(profile, Collections.<IInstallableUnit> emptyList(), new URI[0], false); int cancelAnswer = Integer.parseInt(properties.getProperty("checkMigration.cancelAnswer")); wizardPage.rememberCancellationDecision(cancelAnswer); } private IStatus checkSystemProperties() { final String ABSENT_SYS_PROPERTY = "not.sysprop."; final String PRESENT_SYS_PROPERTY = "sysprop."; MultiStatus result = new MultiStatus(Activator.PLUGIN_ID, IStatus.ERROR, "System properties validation", null); Set<Entry<Object, Object>> entries = properties.entrySet(); for (Entry<Object, Object> entry : entries) { String key = (String) entry.getKey(); if (key.startsWith(ABSENT_SYS_PROPERTY)) { String property = key.substring(ABSENT_SYS_PROPERTY.length()); if (System.getProperty(property) != null) result.add(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Property " + property + " should not be set.")); } if (key.startsWith(PRESENT_SYS_PROPERTY)) { String property = key.substring(PRESENT_SYS_PROPERTY.length()); String foundValue = System.getProperty(property); if (!entry.getValue().equals(foundValue)) result.add(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Property " + property + " should be set to " + entry.getValue() + " and is set to " + foundValue + ".")); } } if (result.getChildren().length == 0) return Status.OK_STATUS; return result; } private IStatus checkAbsenceOfBundles() { MultiStatus result = new MultiStatus(Activator.PLUGIN_ID, IStatus.ERROR, "Some bundles should not be there", null); String unexpectedBundlesString = properties.getProperty("unexpectedBundleList"); if (unexpectedBundlesString == null) return Status.OK_STATUS; String[] unexpectedBundles = unexpectedBundlesString.split(","); for (String bsn : unexpectedBundles) { if (containsBundle(bsn)) { result.add(new Status(IStatus.ERROR, Activator.PLUGIN_ID, bsn + " should not have been found in the install")); } } if (result.getChildren().length == 0) return Status.OK_STATUS; return result; } private boolean containsBundle(final String bsn) { FrameworkWiring fWiring = Activator.getBundleContext().getBundle(Constants.SYSTEM_BUNDLE_LOCATION).adapt(FrameworkWiring.class); Collection<BundleCapability> existing = fWiring.findProviders(new Requirement() { public String getNamespace() { return IdentityNamespace.IDENTITY_NAMESPACE; } public Map<String, String> getDirectives() { return Collections.singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE, "(" + IdentityNamespace.IDENTITY_NAMESPACE + "=" + bsn + ")"); } public Map<String, Object> getAttributes() { return Collections.EMPTY_MAP; } public Resource getResource() { return null; } }); return !existing.isEmpty(); } private IStatus checkPresenceOfBundles() { MultiStatus result = new MultiStatus(Activator.PLUGIN_ID, IStatus.ERROR, "Some bundles should not be there", null); String expectedBundlesString = properties.getProperty("expectedBundleList"); if (expectedBundlesString == null) return Status.OK_STATUS; String[] expectedBundles = expectedBundlesString.split(","); for (String bsn : expectedBundles) { if (!containsBundle(bsn)) { result.add(new Status(IStatus.ERROR, Activator.PLUGIN_ID, bsn + " is missing from the install")); } } if (result.getChildren().length == 0) return Status.OK_STATUS; return result; } private IStatus hasProfileFlag() { if (properties.getProperty("checkProfileResetFlag") == null || "false".equals(properties.getProperty("checkProfileResetFlag"))) return Status.OK_STATUS; //Make sure that the profile is already loaded IProfileRegistry reg = (IProfileRegistry) agent.getService(IProfileRegistry.SERVICE_NAME); IProfile profile = reg.getProfile(IProfileRegistry.SELF); String profileId = profile.getProfileId(); long history[] = reg.listProfileTimestamps(profileId); long lastTimestamp = history[history.length - 1]; if (IProfile.STATE_SHARED_INSTALL_VALUE_NEW.equals(reg.getProfileStateProperties(profileId, lastTimestamp).get(IProfile.STATE_PROP_SHARED_INSTALL))) { return Status.OK_STATUS; } if (history.length == 1) { return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "The flag indicating that a profile has been reset is incorrectly setup"); } long previousToLastTimestamp = history[history.length - 2]; if (IProfile.STATE_SHARED_INSTALL_VALUE_NEW.equals(reg.getProfileStateProperties(profileId, previousToLastTimestamp).get(IProfile.STATE_PROP_SHARED_INSTALL))) { return Status.OK_STATUS; } return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "The flag indicating that a profile has been reset is incorrectly setup"); } private IStatus checkMigrationWizard() { if (properties.getProperty("checkMigrationWizard") == null) return Status.OK_STATUS; IProfileRegistry reg = (IProfileRegistry) agent.getService(IProfileRegistry.SERVICE_NAME); IProfile profile = reg.getProfile(IProfileRegistry.SELF); //Fake the opening of the wizard MigrationWizardTestHelper migrationSupport = new MigrationWizardTestHelper(); migrationSupport.performMigration(agent, reg, profile); boolean wizardExpectedToOpen = Boolean.parseBoolean(properties.getProperty("checkMigrationWizard.open")); if (migrationSupport.wizardOpened == wizardExpectedToOpen) return Status.OK_STATUS; return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "The migration wizard did " + (wizardExpectedToOpen ? "not" : "") + " open"); } private void assumeMigrated() { if (properties.getProperty("checkMigrationWizard.simulate.reinstall") == null) return; new MigrationWizardTestHelper().rememberMigrationCompleted(); } }