/******************************************************************************* * Copyright (c) 2005, 2017 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 *******************************************************************************/ package org.eclipse.osgi.internal.hookregistry; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Dictionary; import org.eclipse.osgi.container.Module; import org.eclipse.osgi.container.ModuleContainer; import org.eclipse.osgi.container.ModuleContainerAdaptor; import org.eclipse.osgi.container.ModuleContainerAdaptor.ModuleEvent; import org.eclipse.osgi.container.ModuleRevision; import org.eclipse.osgi.container.ModuleRevisionBuilder; import org.eclipse.osgi.storage.BundleInfo.Generation; import org.osgi.framework.BundleException; /** * A StorageHookFactory hooks into the persistent storage loading and saving of bundle {@link Generation generations}. * A factory creates StorageHook instances that get associated with each Generation object installed.

* @see Generation#getStorageHook(Class) * @param the StorageHook type * @param the load context type * @param the save context type */ public abstract class StorageHookFactory> { protected final String KEY = this.getClass().getName().intern(); /** * Returns the storage version of this storage hook. This version * is used by the storage to check the consistency of cached persistent * data. Any time a storage hook changes the format of its persistent * data the storage version should be incremented. * @return the storage version of this storage hook */ public abstract int getStorageVersion(); /** * Returns the implementation class name for the hook implementation * @return the implementation class name for the hook implementation */ public final String getKey() { return KEY; } /** * Returns true if the persisted version is compatible with the * current version of this storage hook. The default implementation * returns true if the specified version is identical to the current * version. Implementations must override this method if they * want to support other (older) versions for migration purposes. * @param version the persisted version * @return true if the persisted version is compatible with * the current version. */ public boolean isCompatibleWith(int version) { return getStorageVersion() == version; } /** * Creates a save context object for a storage hook. The * save context is passed to the {@link StorageHook#save(Object, DataOutputStream)} * for each generation being persisted by the framework. * @return a save context object */ public S createSaveContext() { return null; } /** * Creates a load context object for a storage hook. The * load context is passed to the {@link StorageHook#load(Object, DataInputStream)} * for each generation being loaded from persistent storage * by the framework. * @param version the persistent version * @return the load context object */ public L createLoadContext(int version) { return null; } /** * Creates a storage hook for the specified generation. * @param generation the generation for the storage hook * @return a storage hook */ protected abstract H createStorageHook(Generation generation); /** * Creates a storage hook for the specified generation and checks that the * factory class of the storage hook equals the class of this storage hook * factory. * * @param generation - The generation for which a storage hook should be * created. * @return A newly created storage hook. * @throws IllegalStateException - If the factory class of the storage hook * is not equal to the class of this storage hook factory. */ public final H createStorageHookAndValidateFactoryClass(Generation generation) { H result = createStorageHook(generation); Class factoryClass = getClass(); Class factoryClassOfStorageHook = result.getFactoryClass(); if (!factoryClass.equals(factoryClassOfStorageHook)) throw new IllegalStateException(String.format("The factory class '%s' of storage hook '%s' does not match the creating factory class of '%s'", factoryClassOfStorageHook.getName(), result, factoryClass.getName())); //$NON-NLS-1$ return result; } /** * A storage hook for a specific generation object. This hook * is responsible for persisting and loading data associated * with a specific generation. * * @param the save context type * @param the load context type */ public static abstract class StorageHook { private final Class>> factoryClass; private final Generation generation; public StorageHook(Generation generation, Class>> factoryClass) { this.generation = generation; this.factoryClass = factoryClass; } /** * The generation associated with this hook. * @return the generation associated with this hook. */ public Generation getGeneration() { return generation; } /** * Initializes this storage hook with the content of the specified bundle manifest. * This method is called when a bundle is installed or updated. * @param manifest the bundle manifest to load into this storage hook * @throws BundleException if any error occurs */ public abstract void initialize(Dictionary manifest) throws BundleException; /** * Allows a builder to be modified before it is used by the framework to create a {@link ModuleRevision revision} * associated with the bundle {@link #getGeneration() generation} being installed or updated. * @param operation The lifecycle operation event that is in progress using the supplied builder. * This will be either {@link ModuleEvent#INSTALLED installed} or {@link ModuleEvent#UPDATED updated}. * @param origin The module which originated the lifecycle operation. The origin may be {@code null} for * {@link ModuleEvent#INSTALLED installed} operations. This is the module * passed to the {@link ModuleContainer#install(Module, String, ModuleRevisionBuilder, Object) install} or * {@link ModuleContainer#update(Module, ModuleRevisionBuilder, Object) update} method. * @param builder the builder that will be used to create a new {@link ModuleRevision}. * @return The modified builder or a completely new builder to be used by the bundle. A {@code null} value * indicates the original builder should be used, which may have been modified by adding requirements or * capabilities. * @see ModuleContainerAdaptor#adaptModuleRevisionBuilder(ModuleEvent, Module, ModuleRevisionBuilder, Object) */ public ModuleRevisionBuilder adaptModuleRevisionBuilder(ModuleEvent operation, Module origin, ModuleRevisionBuilder builder) { // do nothing return null; } /** * Loads the data from the specified * input stream into the storage hook. This method is called during startup to * load all the persistently installed bundles.

* It is important that this method and the {@link #save(Object, DataOutputStream)} method * stay in sync. This method must be able to successfully read the data saved by the * {@link #save(Object, DataOutputStream)} method. * @param is an input stream used to load the storage hook's data from. * @see #save(Object, DataOutputStream) * @throws IOException if any error occurs */ public abstract void load(L loadContext, DataInputStream is) throws IOException; /** * Saves the data from this storage hook into the specified output stream. This method * is called if some persistent data has changed for the bundle.

* It is important that this method and the {@link #load(Object, DataInputStream)} * method stay in sync. This method must be able to save data which the * {@link #load(Object, DataInputStream)} method can ready successfully. * @see #load(Object, DataInputStream) * @param os an output stream used to save the storage hook's data from. * @throws IOException if any error occurs */ public abstract void save(S saveContext, DataOutputStream os) throws IOException; /** * Gets called during {@link Generation#delete()} to inform the hook that the generation * associated with the hook is being deleted. */ public void deletingGeneration() { // do nothing by default } /** * Validates the data in this storage hook, if the data is invalid then an illegal state * exception is thrown * @throws IllegalStateException if the data is invalid */ public void validate() throws IllegalStateException { // do nothing by default } /** * The storage hook factory class of this storage hook * @return the storage hook factory class */ public Class>> getFactoryClass() { return factoryClass; } } }