Java tutorial
/******************************************************************************* * Copyright (c) 2011, 2013 AGETO Service GmbH 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: * Gunnar Wagenknecht - initial API and implementation *******************************************************************************/ package org.eclipse.gyrex.p2.internal; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import org.eclipse.gyrex.common.identifiers.IdHelper; import org.eclipse.gyrex.p2.internal.packages.IPackageManager; import org.eclipse.gyrex.p2.internal.packages.InstallState; import org.eclipse.gyrex.p2.internal.packages.InstallableUnitReference; import org.eclipse.gyrex.p2.internal.packages.PackageDefinition; import org.eclipse.gyrex.preferences.CloudScope; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.equinox.p2.metadata.Version; import org.osgi.service.prefs.BackingStoreException; import org.osgi.service.prefs.Preferences; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * */ public class PackageManager implements IPackageManager { private static final String PREF_NODE_PACKAGES = "packages"; private static final String PREF_NODE_COMPONENTS = "components"; private static final String PREF_KEY_NODE_FILTER = "nodeFilter"; private static final String PREF_KEY_INSTALL_STATE = "installState"; private static final String PREF_KEY_INSTALL_STATE_TIMESTAMP = "installStateTS"; private static final String PREF_KEY_TYPE = "type"; private static final String PREF_KEY_VERSION = "version"; private static final String COMPONENT_TYPE_IU = "IU"; private static final Logger LOG = LoggerFactory.getLogger(PackageManager.class); private static String toRelativeTime(final long duration) { if (duration < TimeUnit.MINUTES.toMillis(2)) return "a minute ago"; else if (duration < TimeUnit.HOURS.toMillis(2)) return String.format("%d minutes ago", TimeUnit.MILLISECONDS.toMinutes(duration)); else return String.format("%d hours ago", TimeUnit.MILLISECONDS.toMinutes(duration)); } @Override public PackageDefinition getPackage(final String id) { try { if (!IdHelper.isValidId(id)) throw new IllegalArgumentException("invalid id"); final IEclipsePreferences rootNode = CloudScope.INSTANCE.getNode(P2Activator.SYMBOLIC_NAME); if (!rootNode.nodeExists(PREF_NODE_PACKAGES)) return null; final Preferences node = rootNode.node(PREF_NODE_PACKAGES); if (!node.nodeExists(id)) return null; return readPackage(id); } catch (final BackingStoreException e) { throw new IllegalStateException( "Error reading package definition from backend store. " + ExceptionUtils.getRootCauseMessage(e), e); } } private Preferences getPackageNode(final String packageId) { return CloudScope.INSTANCE.getNode(P2Activator.SYMBOLIC_NAME).node(PREF_NODE_PACKAGES).node(packageId); } @Override public Collection<PackageDefinition> getPackages() { try { final IEclipsePreferences rootNode = CloudScope.INSTANCE.getNode(P2Activator.SYMBOLIC_NAME); if (!rootNode.nodeExists(PREF_NODE_PACKAGES)) return Collections.emptyList(); final Preferences channelsNode = rootNode.node(PREF_NODE_PACKAGES); final String[] childrenNames = channelsNode.childrenNames(); final List<PackageDefinition> channels = new ArrayList<PackageDefinition>(); for (final String channelId : childrenNames) { final PackageDefinition descriptor = readPackage(channelId); if (descriptor != null) { channels.add(descriptor); } } return channels; } catch (final BackingStoreException e) { throw new IllegalStateException("Error reading package definitions from backend store. " + ExceptionUtils.getRootCauseMessage(e), e); } } @Override public boolean isMarkedForInstall(final PackageDefinition packageDefinition) { try { if (!IdHelper.isValidId(packageDefinition.getId())) throw new IllegalArgumentException("invalid id"); final IEclipsePreferences rootNode = CloudScope.INSTANCE.getNode(P2Activator.SYMBOLIC_NAME); if (!rootNode.nodeExists(PREF_NODE_PACKAGES)) throw new IllegalArgumentException("package does not exist"); final Preferences node = rootNode.node(PREF_NODE_PACKAGES); if (!node.nodeExists(packageDefinition.getId())) throw new IllegalArgumentException("package does not exist"); final Preferences pkgNode = node.node(packageDefinition.getId()); final String installState = pkgNode.get(PREF_KEY_INSTALL_STATE, null); // support old key for backwards compatibility if ((installState == null) && (null != pkgNode.get("install", null))) return pkgNode.getBoolean("install", false); return InstallState.fromString(installState) == InstallState.ROLLOUT; } catch (final BackingStoreException e) { throw new IllegalStateException( "Error reading package definition from backend store. " + ExceptionUtils.getRootCauseMessage(e), e); } } @Override public boolean isMarkedForUninstall(final PackageDefinition packageDefinition) { try { if (!IdHelper.isValidId(packageDefinition.getId())) throw new IllegalArgumentException("invalid id"); final IEclipsePreferences rootNode = CloudScope.INSTANCE.getNode(P2Activator.SYMBOLIC_NAME); if (!rootNode.nodeExists(PREF_NODE_PACKAGES)) throw new IllegalArgumentException("package does not exist"); final Preferences node = rootNode.node(PREF_NODE_PACKAGES); if (!node.nodeExists(packageDefinition.getId())) throw new IllegalArgumentException("package does not exist"); final Preferences pkgNode = node.node(packageDefinition.getId()); final String installState = pkgNode.get(PREF_KEY_INSTALL_STATE, null); // support old key for backwards compatibility if ((installState == null) && (null != pkgNode.get("uninstall", null))) return pkgNode.getBoolean("uninstall", false); return InstallState.fromString(installState) == InstallState.REVOKE; } catch (final BackingStoreException e) { throw new IllegalStateException( "Error reading package definition from backend store. " + ExceptionUtils.getRootCauseMessage(e), e); } } private boolean isPackageNodeAvailable(final String packageId) throws BackingStoreException { final IEclipsePreferences rootNode = CloudScope.INSTANCE.getNode(P2Activator.SYMBOLIC_NAME); if (!rootNode.nodeExists(PREF_NODE_PACKAGES)) return false; return rootNode.node(PREF_NODE_PACKAGES).nodeExists(packageId); } @Override public void markedForInstall(final PackageDefinition packageDefinition) { try { if (!IdHelper.isValidId(packageDefinition.getId())) throw new IllegalArgumentException("invalid id"); if (!isPackageNodeAvailable(packageDefinition.getId())) throw new IllegalArgumentException("package does not exist"); final Preferences pkgNode = getPackageNode(packageDefinition.getId()); pkgNode.put(PREF_KEY_INSTALL_STATE, InstallState.toString(InstallState.ROLLOUT)); pkgNode.putLong(PREF_KEY_INSTALL_STATE_TIMESTAMP, System.currentTimeMillis()); // cleanup legacy keys pkgNode.remove("install"); pkgNode.remove("uninstall"); pkgNode.flush(); } catch (final BackingStoreException e) { throw new IllegalStateException( "Error reading package definition from backend store. " + ExceptionUtils.getRootCauseMessage(e), e); } } @Override public void markedForUninstall(final PackageDefinition packageDefinition) { try { if (!IdHelper.isValidId(packageDefinition.getId())) throw new IllegalArgumentException("invalid id"); if (!isPackageNodeAvailable(packageDefinition.getId())) throw new IllegalArgumentException("package does not exist"); final Preferences pkgNode = getPackageNode(packageDefinition.getId()); pkgNode.put(PREF_KEY_INSTALL_STATE, InstallState.toString(InstallState.REVOKE)); pkgNode.putLong(PREF_KEY_INSTALL_STATE_TIMESTAMP, System.currentTimeMillis()); // cleanup legacy keys pkgNode.remove("install"); pkgNode.remove("uninstall"); pkgNode.flush(); } catch (final BackingStoreException e) { throw new IllegalStateException( "Error reading package definition from backend store. " + ExceptionUtils.getRootCauseMessage(e), e); } } private PackageDefinition readPackage(final String id) { try { final PackageDefinition pkgDefinition = new PackageDefinition(); pkgDefinition.setId(id); final Preferences node = getPackageNode(id); pkgDefinition.setNodeFilter(node.get(PREF_KEY_NODE_FILTER, null)); pkgDefinition.setInstallState(InstallState.fromString(node.get(PREF_KEY_INSTALL_STATE, id))); if (node.nodeExists(PREF_NODE_COMPONENTS)) { final Preferences componentsNode = node.node(PREF_NODE_COMPONENTS); for (final String componentId : componentsNode.childrenNames()) { final Preferences componentNode = componentsNode.node(componentId); final String type = componentNode.get(PREF_KEY_TYPE, null); if (StringUtils.equals(COMPONENT_TYPE_IU, type)) { final InstallableUnitReference iu = new InstallableUnitReference(); iu.setId(componentId); final String version = componentNode.get(PREF_KEY_VERSION, null); if (version != null) { iu.setVersion(Version.create(version)); } pkgDefinition.addComponentToInstall(iu); } } } return pkgDefinition; } catch (final Exception e) { LOG.warn("Unable to read package definition {}. {}", id, ExceptionUtils.getRootCauseMessage(e)); return null; } } @Override public void removePackage(final String id) { try { if (!IdHelper.isValidId(id)) throw new IllegalArgumentException("invalid id"); final IEclipsePreferences rootNode = CloudScope.INSTANCE.getNode(P2Activator.SYMBOLIC_NAME); if (!rootNode.nodeExists(PREF_NODE_PACKAGES)) return; final Preferences node = rootNode.node(PREF_NODE_PACKAGES); if (!node.nodeExists(id)) return; final IStatus modifiable = verifyPackageIsModifiable(id); if (!modifiable.isOK()) throw new IllegalStateException(modifiable.getMessage()); node.node(id).removeNode(); node.flush(); } catch (final BackingStoreException e) { throw new IllegalStateException("Error removing package definition from backend store. " + ExceptionUtils.getRootCauseMessage(e), e); } } @Override public void savePackage(final PackageDefinition packageDefinition) { try { final String id = packageDefinition.getId(); if (!IdHelper.isValidId(id)) throw new IllegalArgumentException("invalid id"); // prevent updating an existing package if it's not modifiable if (isPackageNodeAvailable(id)) { final IStatus modifiable = verifyPackageIsModifiable(id); if (!modifiable.isOK()) throw new IllegalStateException(modifiable.getMessage()); } final Collection<InstallableUnitReference> componentsToInstall = packageDefinition .getComponentsToInstall(); final Preferences node = getPackageNode(id); final String nodeFilter = packageDefinition.getNodeFilter(); if (StringUtils.isNotBlank(nodeFilter)) { node.put(PREF_KEY_NODE_FILTER, nodeFilter); } else { node.remove(PREF_KEY_NODE_FILTER); } final Preferences componentsToInstallNode = node.node(PREF_NODE_COMPONENTS); final Set<String> componentsWritten = new HashSet<String>(); for (final InstallableUnitReference component : componentsToInstall) { componentsWritten.add(component.getId()); final Preferences componentNode = componentsToInstallNode.node(component.getId()); componentNode.put(PREF_KEY_TYPE, COMPONENT_TYPE_IU); final InstallableUnitReference iu = component; final Version version = iu.getVersion(); if (null != version) { final StringBuffer versionString = new StringBuffer(); version.toString(versionString); componentNode.put(PREF_KEY_VERSION, versionString.toString()); } else { componentNode.remove(PREF_KEY_VERSION); } } for (final String child : componentsToInstallNode.childrenNames()) { if (!componentsWritten.contains(child)) { componentsToInstallNode.node(child).removeNode(); } } node.flush(); } catch (final BackingStoreException e) { throw new IllegalStateException( "Error saving package definition to backend store. " + ExceptionUtils.getRootCauseMessage(e), e); } } @Override public IStatus verifyPackageIsModifiable(final String id) throws IllegalStateException, IllegalArgumentException { if (!IdHelper.isValidId(id)) return new Status(IStatus.ERROR, P2Activator.SYMBOLIC_NAME, "invalid package id"); try { if (!isPackageNodeAvailable(id)) return new Status(IStatus.ERROR, P2Activator.SYMBOLIC_NAME, "package does not exist"); final Preferences pkgNode = getPackageNode(id); final InstallState installState = InstallState.fromString(pkgNode.get(PREF_KEY_INSTALL_STATE, null)); if (installState == InstallState.ROLLOUT) return new Status(IStatus.ERROR, P2Activator.SYMBOLIC_NAME, String.format("Package '%s' is marked for rollout! Please revoke it first.", id)); else if (installState == InstallState.REVOKE) { final long revokeDuration = System.currentTimeMillis() - pkgNode.getLong(PREF_KEY_INSTALL_STATE_TIMESTAMP, 0); if (revokeDuration < TimeUnit.HOURS.toMillis(48)) return new Status(IStatus.ERROR, P2Activator.SYMBOLIC_NAME, String.format( "Package '%s' was revoked %s! Please wait at least 48 hours before modifying a revoked package!", id, toRelativeTime(revokeDuration))); } return Status.OK_STATUS; } catch (final BackingStoreException e) { return new Status(IStatus.ERROR, P2Activator.SYMBOLIC_NAME, String.format("Error accessing backend store. Unable to verify package modifiability. %s", e.getMessage()), e); } } }