/*******************************************************************************
* Copyright (c) 2011 Gerd Wuetherich (gerd@gerd-wuetherich.de).
* 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:
* Gerd Wuetherich (gerd@gerd-wuetherich.de) - initial API and implementation
******************************************************************************/
package org.bundlemaker.core.store.db4o.internal;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import org.bundlemaker.core.BundleMakerCore;
import org.bundlemaker.core.common.classloading.BundleDelegatingClassLoader;
import org.bundlemaker.core.common.classloading.CompoundClassLoader;
import org.bundlemaker.core.internal.modelext.ModelExtFactory;
import org.bundlemaker.core.internal.resource.Resource;
import org.bundlemaker.core.project.internal.DefaultProjectContentResource;
import org.bundlemaker.core.spi.parser.IParsableResource;
import org.bundlemaker.core.spi.store.IPersistentDependencyStore;
import org.eclipse.core.runtime.Assert;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import com.db4o.Db4o;
import com.db4o.ObjectContainer;
import com.db4o.config.Configuration;
import com.db4o.ext.DatabaseClosedException;
import com.db4o.ext.DatabaseReadOnlyException;
import com.db4o.ext.Db4oIOException;
import com.db4o.query.Query;
import com.db4o.reflect.jdk.JdkReflector;
/**
* <p>
* </p>
*
* @author Gerd Wütherich (gerd@gerd-wuetherich.de)
*/
public class PersistentDependencyStoreImpl implements IPersistentDependencyStore {
/** the (absolute) file name of the database file */
private String _fileName;
/** the db4o based database */
private ObjectContainer _database;
/** indicates if the store is initialized */
private boolean _isInitialized = false;
/** - */
private BundleContext _bundleContext;
/**
* <p>
* Creates a new instance of type {@link PersistentDependencyStoreImpl}.
* </p>
*
* @param fileName
*/
public PersistentDependencyStoreImpl(String fileName, BundleContext bundleContext) {
Assert.isNotNull(fileName);
Assert.isNotNull(bundleContext);
//
_fileName = fileName;
_bundleContext = bundleContext;
}
/**
* <p>
* Creates a new instance of type {@link PersistentDependencyStoreImpl}.
* </p>
*
* @param fileName
* the file name
*/
public PersistentDependencyStoreImpl(String fileName) {
Assert.isNotNull(fileName);
_fileName = fileName;
}
/**
* <p>
* Initializes the {@link IPersistentDependencyStore}.
* </p>
*/
public final void init() {
// create a new configuration
Configuration configuration = Db4o.newConfiguration();
ClassLoader classLoader = getReflectorClassLoader();
configuration.reflectWith(new JdkReflector(classLoader));
try {
// set cascade on update
configuration.objectClass(Resource.class).cascadeOnUpdate(true);
configuration.objectClass(DefaultProjectContentResource.class).cascadeOnUpdate(true);
// set cascade on activation
configuration.objectClass(Resource.class).cascadeOnActivate(true);
configuration.objectClass(DefaultProjectContentResource.class).cascadeOnActivate(true);
// configure db4o with additional persistent classes
String[][] classes = getAdditionalPersistentClasses();
for (String clazz : classes[0]) {
configuration.objectClass(classLoader.loadClass(clazz)).cascadeOnUpdate(true);
}
for (String clazz : classes[1]) {
configuration.objectClass(classLoader.loadClass(clazz)).cascadeOnActivate(true);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// note: cascadeOnDelete is not possible, otherwise we clean up
// FlyWeightCache instances!
// set the activation depth
// since we really want the whole db in memory, set the activation
// depth very high
configuration.activationDepth(Integer.MAX_VALUE);
// open file
File file = new File(_fileName);
file.getParentFile().mkdirs();
// we have to use the Db4o methods here as the Db4o OSGi service does
// handle the class loading right ,-/
_database = Db4o.openFile(configuration, _fileName);
// set initialized
_isInitialized = true;
}
/**
* <p>
* Returns <code>true</code> if this store is initialized.
* </p>
*
* @return <code>true</code> if this store is initialized.
*/
public final boolean isInitialized() {
return _isInitialized;
}
/**
* <p>
* Disposes the dependency store.
* </p>
*/
public final void dispose() {
try {
// commit all changes
_database.commit();
// close the database
_database.close();
} catch (Db4oIOException e) {
} catch (DatabaseClosedException e) {
} catch (DatabaseReadOnlyException e) {
}
// set initialized to false
_isInitialized = false;
}
/**
* {@inheritDoc}
*/
public final void commit() {
Assert.isTrue(isInitialized(), "The persistent dependency store has not been initialized.");
// commits the database
_database.commit();
}
/**
* <p>
* Returns the database.
* </p>
*
* @return the database.
*/
protected final ObjectContainer getDatabase() {
return _database;
}
/**
* {@inheritDoc}
*/
public List<IParsableResource> getResources() {
Query query = getDatabase().query();
query.constrain(Resource.class);
List<IParsableResource> result = query.execute();
//
return result;
}
/**
* {@inheritDoc}
*/
public void updateResource(IParsableResource bundleElement) {
getDatabase().store(bundleElement);
}
/**
* {@inheritDoc}
*/
public void delete(IParsableResource resource) {
getDatabase().delete(resource);
}
/**
* <p>
* Creates the reflector class loader.
* </p>
*
* @return the reflector class loader.
*/
private ClassLoader getReflectorClassLoader() {
//
CompoundClassLoader compoundClassLoader = new CompoundClassLoader();
//
for (Bundle bundle : getExtensionBundles()) {
compoundClassLoader.add(new BundleDelegatingClassLoader(bundle));
}
//
return compoundClassLoader;
}
/**
* <p>
* Returns a list with all the bundles that define model extensions.
* </p>
*
* @return the reflector class loader.
*/
private List<Bundle> getExtensionBundles() {
// get the namespace list
List<String> namespaces = ModelExtFactory.getModelExtensionFactory().getExtensionBundleNamespaces();
// create the result
List<Bundle> result = new LinkedList<Bundle>();
// add the bundles
for (Bundle bundle : _bundleContext.getBundles()) {
if (namespaces.contains(bundle.getSymbolicName()) || BundleMakerCore.BUNDLE_ID_BUNDLEMAKER_CORE.equals(bundle.getSymbolicName())) {
System.out.println(" - " + bundle.getSymbolicName());
result.add(bundle);
}
}
// return the result
return result;
}
/**
* <p>
* Returns a two dimensional array that contains the additional persistent classes.
* </p>
*
* @return
*/
private String[][] getAdditionalPersistentClasses() {
//
List<String> activateClasses = new LinkedList<String>();
List<String> updateClasses = new LinkedList<String>();
//
for (Bundle bundle : getExtensionBundles()) {
URL url = bundle.getResource("store-db4o.properties");
if (url != null) {
try {
Properties properties = new Properties();
InputStream inputStream = url.openStream();
properties.load(inputStream);
inputStream.close();
if (properties.containsKey("cascadeOnUpdate")) {
String prop = properties.getProperty("cascadeOnUpdate");
String[] values = prop.split(",");
try {
for (String value : values) {
updateClasses.add(value.trim());
}
} catch (Exception e) {
e.printStackTrace();
}
}
if (properties.containsKey("cascadeOnActivate")) {
String prop = properties.getProperty("cascadeOnActivate");
String[] values = prop.split(",");
try {
for (String value : values) {
activateClasses.add(value.trim());
}
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (IOException e) {
//
}
}
}
// create the result
String[][] result = new String[2][];
result[0] = updateClasses.toArray(new String[0]);
result[1] = activateClasses.toArray(new String[0]);
return result;
}
}