blob: eaf97101e7986753fa01c5f6e53977965479edde [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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.<p>
* @see Generation#getStorageHook(Class)
* @param <S> the StorageHook type
* @param <L> the load context type
* @param <H> the save context type
*/
public abstract class StorageHookFactory<S, L, H extends StorageHookFactory.StorageHook<S, L>> {
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 <S> the save context type
* @param <L> the load context type
*/
public static abstract class StorageHook<S, L> {
private final Class<? extends StorageHookFactory<S, L, ? extends StorageHook<S, L>>> factoryClass;
private final Generation generation;
public StorageHook(Generation generation, Class<? extends StorageHookFactory<S, L, ? extends StorageHook<S, L>>> 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<String, String> 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. <p>
* 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. <p>
* 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<? extends StorageHookFactory<S, L, ? extends StorageHook<S, L>>> getFactoryClass() {
return factoryClass;
}
}
}