All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bimserver.database.Database Maven / Gradle / Ivy

There is a newer version: 1.5.9
Show newest version
package org.bimserver.database;

/******************************************************************************
 * Copyright (C) 2009-2016  BIMserver.org
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see {@literal}.
 *****************************************************************************/

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.bimserver.BimServer;
import org.bimserver.BimserverDatabaseException;
import org.bimserver.database.actions.AddUserDatabaseAction;
import org.bimserver.database.actions.CreateBaseProjectDatabaseAction;
import org.bimserver.database.berkeley.DatabaseInitException;
import org.bimserver.database.migrations.InconsistentModelsException;
import org.bimserver.database.migrations.MigrationException;
import org.bimserver.database.migrations.Migrator;
import org.bimserver.emf.MetaDataManager;
import org.bimserver.models.geometry.GeometryPackage;
import org.bimserver.models.ifc2x3tc1.Ifc2x3tc1Package;
import org.bimserver.models.ifc4.Ifc4Package;
import org.bimserver.models.log.AccessMethod;
import org.bimserver.models.log.DatabaseCreated;
import org.bimserver.models.log.LogPackage;
import org.bimserver.models.store.ServerSettings;
import org.bimserver.models.store.SmtpProtocol;
import org.bimserver.models.store.StorePackage;
import org.bimserver.models.store.User;
import org.bimserver.models.store.UserType;
import org.bimserver.shared.exceptions.UserException;
import org.bimserver.utils.BinUtils;
import org.bimserver.utils.DoubleHashMap;
import org.bimserver.webservices.authorization.SystemAuthorization;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sleepycat.je.DatabaseNotFoundException;

public class Database implements BimDatabase {

	private static final Logger LOGGER = LoggerFactory.getLogger(Database.class);
	private static final String CLASS_LOOKUP_TABLE = "INT-ClassLookup";
	public static final String STORE_PROJECT_NAME = "INT-Store";
	public static final int STORE_PROJECT_ID = 1;
	public static final String SCHEMA_VERSION = "SCHEMA_VERSION";
	private static final String DATE_CREATED = "DATE_CREATED";
	private final Map emfPackages = new LinkedHashMap();
	private final KeyValueStore keyValueStore;
	private final DoubleHashMap classifiers = new DoubleHashMap();
	private final List realClasses = new ArrayList();
	private final Map oidCounters = new HashMap();
	private final AtomicInteger pidCounter = new AtomicInteger(1);
	private final Registry registry;
	private Date created;
	private final Set sessions = Collections.newSetFromMap(new ConcurrentHashMap());
	private int databaseSchemaVersion;
	private short tableId;
	private Migrator migrator;
	private final MetaDataManager metaDataManager;
	private final BimServer bimServer;

	/*
	 * This variable should be _incremented_ with every (released)
	 * database-schema change. Do not change this variable when nothing has
	 * changed in the schema!
	 */
	public static final int APPLICATION_SCHEMA_VERSION = 22;

	public Database(BimServer bimServer, Set emfPackages, KeyValueStore keyValueStore, MetaDataManager metaDataManager) throws DatabaseInitException {
		this.bimServer = bimServer;
		this.keyValueStore = keyValueStore;
		this.metaDataManager = metaDataManager;
		this.emfPackages.put(StorePackage.eINSTANCE.getName(), StorePackage.eINSTANCE);
		this.emfPackages.put(LogPackage.eINSTANCE.getName(), LogPackage.eINSTANCE);
		this.emfPackages.put(GeometryPackage.eINSTANCE.getName(), GeometryPackage.eINSTANCE);
		for (EPackage ePackage : emfPackages) {
			this.emfPackages.put(ePackage.getName(), ePackage);
		}
		this.registry = new Registry(keyValueStore);
	}

	public int getApplicationSchemaVersion() {
		return APPLICATION_SCHEMA_VERSION;
	}

	public int getDatabaseSchemaVersion() {
		return databaseSchemaVersion;
	}

	public EClass getEClassForName(String packageName, String className) {
		EPackage ePackage = emfPackages.get(packageName);
		if (ePackage.getEClassifier(className) != null) {
			return (EClass) ePackage.getEClassifier(className);
		}
		return null;
	}

	public void init() throws DatabaseInitException, DatabaseRestartRequiredException, InconsistentModelsException {
		DatabaseSession databaseSession = createSession();
		try {
			if (getKeyValueStore().isNew()) {
				keyValueStore.createTable(CLASS_LOOKUP_TABLE, null);
				keyValueStore.createTable(Database.STORE_PROJECT_NAME, null);
				keyValueStore.createTable(Registry.REGISTRY_TABLE, null);
				setDatabaseVersion(-1, databaseSession);
				created = new Date();
				registry.save(DATE_CREATED, created, databaseSession);
			} else {
				keyValueStore.openTable(CLASS_LOOKUP_TABLE);
				keyValueStore.openTable(Database.STORE_PROJECT_NAME);
				keyValueStore.openTable(Registry.REGISTRY_TABLE);
				created = registry.readDate(DATE_CREATED, databaseSession);
				if (created == null) {
					created = new Date();
					registry.save(DATE_CREATED, created, databaseSession);
				}
			}
			
			databaseSchemaVersion = registry.readInt(SCHEMA_VERSION, databaseSession, -1);
			
			migrator = new Migrator(this);

			if (getKeyValueStore().isNew()) {
				try {
					migrator.migrate(databaseSession);
				} catch (MigrationException e) {
					LOGGER.error("", e);
				}
				registry.save("isnew", true, databaseSession);
				databaseSession.commit();
				databaseSession.close();
				throw new DatabaseRestartRequiredException();
			} else if (registry.readBoolean("isnew", true, databaseSession)) {
				initInternalStructure(databaseSession);
				initCounters(databaseSession);

				ServerSettings settings = createDefaultSettings(databaseSession);
				databaseSession.store(settings);
				
				new CreateBaseProjectDatabaseAction(databaseSession, AccessMethod.INTERNAL).execute();
				AddUserDatabaseAction addUserDatabaseAction = new AddUserDatabaseAction(bimServer, databaseSession, AccessMethod.INTERNAL, "system", "system", "System", UserType.SYSTEM, new SystemAuthorization(1, TimeUnit.HOURS), false, null);
				addUserDatabaseAction.setCreateSystemUser();
				User systemUser = addUserDatabaseAction.execute();
				systemUser.setCreatedBy(systemUser);
				databaseSession.store(systemUser);

				DatabaseCreated databaseCreated = databaseSession.create(DatabaseCreated.class);
				databaseCreated.setAccessMethod(AccessMethod.INTERNAL);
				databaseCreated.setExecutor(systemUser);
				databaseCreated.setDate(new Date());
				databaseCreated.setPath(getKeyValueStore().getLocation());
				databaseCreated.setVersion(databaseSchemaVersion);
				databaseSession.store(databaseCreated);

				registry.save("isnew", false, databaseSession);
			} else {
				initInternalStructure(databaseSession);
				initCounters(databaseSession);
			}
			for (EClass eClass : classifiers.keyBSet()) {
				if (eClass.getEPackage() == Ifc2x3tc1Package.eINSTANCE || eClass.getEPackage() == Ifc4Package.eINSTANCE) {
					realClasses.add(eClass.getName());
				}
			}
			databaseSession.commit();
		} catch (UserException e) {
			LOGGER.error("", e);
			close();
			throw new DatabaseInitException(e.getMessage());
		} catch (BimserverDatabaseException e) {
			LOGGER.error("", e);
			close();
			throw new DatabaseInitException(e.getMessage());
		} catch (DatabaseRestartRequiredException e) {
			throw e;
		} catch (Exception e) {
			throw new DatabaseInitException(e);
		} finally {
			databaseSession.close();
		}
	}

	public ServerSettings createDefaultSettings(DatabaseSession databaseSession) throws BimserverDatabaseException {
		ServerSettings settings = databaseSession.create(ServerSettings.class);
		settings.setEmailSenderAddress("[email protected]");
		settings.setEmailSenderName("Administrator");
		settings.setSiteAddress("");
		settings.setSmtpServer("");
		settings.setSmtpPort(25);
		settings.setSmtpProtocol(SmtpProtocol.SMTP);
		settings.setProtocolBuffersPort(-1);
		settings.setAllowCreateValidatedUser(true);
		settings.setAllowSelfRegistration(false);
		settings.setAllowUsersToCreateTopLevelProjects(false);
		settings.setCheckinMergingEnabled(false);
		settings.setHideUserListForNonAdmin(true);
		settings.setCacheOutputFiles(true);
		settings.setServiceRepositoryUrl("https://raw.githubusercontent.com/opensourceBIM/BIMserver-Repository/master");
		settings.setAllowOnlyWhitelisted(false);
		settings.setGenerateGeometryOnCheckin(true);
		settings.setReuseGeometry(true);
		settings.setRenderEngineProcesses(1);
		settings.setSessionTimeOutSeconds(60 * 60 * 24 * 30); // 1 month
		settings.getWhitelistedDomains().add("localhost");
		settings.getWhitelistedDomains().add("localhost:8080");
		return settings;
	}
	
	public long newOid(EClass eClass) {
		return oidCounters.get(eClass).addAndGet(65536);
	}

	public long getCounter(EClass eClass) {
		return oidCounters.get(eClass).get();
	}

	public EClassifier getEClassifier(String packageName, String classifierName) throws BimserverDatabaseException {
		if (packageName == null) {
			throw new BimserverDatabaseException("No package name given");
		}
		if (packageName.contains(".")) {
			packageName = packageName.substring(packageName.lastIndexOf(".") + 1);
		}
		EPackage ePackage = emfPackages.get(packageName);
		if (ePackage == null) {
			throw new BimserverDatabaseException("No package found with name " + packageName);
		}
		if (ePackage.getEClassifier(classifierName) != null) {
			return ePackage.getEClassifier(classifierName);
		}
		throw new BimserverDatabaseException("No classifier found with name " + classifierName + " in package " + packageName);
	}

	public EClass getEClass(String packageName, String classifierName) throws BimserverDatabaseException {
		EClassifier eClassifier = getEClassifier(packageName, classifierName);
		if (eClassifier instanceof EClass) {
			return (EClass)eClassifier;
		}
		throw new BimserverDatabaseException("Classifier " + packageName + "." + classifierName + " is not an EClass");
	}

	public void initInternalStructure(DatabaseSession databaseSession) throws BimserverLockConflictException, BimserverDatabaseException {
		RecordIterator recordIterator = keyValueStore.getRecordIterator(CLASS_LOOKUP_TABLE, databaseSession);
		try {
			Record record = recordIterator.next();
			while (record != null) {
				String packageAndClassName = BinUtils.byteArrayToString(record.getValue());
				String packageName = packageAndClassName.substring(0, packageAndClassName.indexOf("_"));
				String className = packageAndClassName.substring(packageAndClassName.indexOf("_") + 1);
				EClass eClass = (EClass) getEClassifier(packageName, className);
				keyValueStore.openTable(packageAndClassName);
				
				for (EStructuralFeature eStructuralFeature : eClass.getEAllStructuralFeatures()) {
					if (eStructuralFeature.getEAnnotation("singleindex") != null) {
						String indexTableName = eClass.getEPackage().getName() + "_" + eClass.getName() + "_" + eStructuralFeature.getName();
						try {
							keyValueStore.openIndexTable(indexTableName);
						} catch (DatabaseNotFoundException e) {
						}
					}
				}
				
				Short cid = BinUtils.byteArrayToShort(record.getKey());
				classifiers.put(cid, eClass);
				record = recordIterator.next();
			}
		} finally {
			recordIterator.close();
		}
	}

	public void initCounters(DatabaseSession databaseSession) throws BimserverLockConflictException, BimserverDatabaseException {
		for (EClass eClass : classifiers.keyBSet()) {
			RecordIterator iterator = keyValueStore.getRecordIterator(eClass.getEPackage().getName() + "_" + eClass.getName(), databaseSession);
			try {
				Record record = iterator.last();
				initCounter(eClass);
				if (record != null) {
					ByteBuffer buffer = ByteBuffer.wrap(record.getKey());
					int pid = buffer.getInt();
					long oid = buffer.getLong();
					if (oid > oidCounters.get(eClass).get()) {
						oidCounters.put(eClass, new AtomicLong(oid));
					}
					if (pid > pidCounter.get()) {
						pidCounter.set(pid);
					}
				}
			} finally {
				iterator.close();
			}
		}
	}

	private void initCounter(EClass eClass) {
		ByteBuffer cidBuffer = ByteBuffer.wrap(new byte[8]);
		cidBuffer.putShort(6, getCidOfEClass(eClass));
		long startOid = cidBuffer.getLong(0);
		oidCounters.put(eClass, new AtomicLong(startOid));
	}

	public int newPid() {
		return pidCounter.incrementAndGet();
	}

	public void close() {
		keyValueStore.close();
	}

	public List getAvailableClasses() {
		return realClasses;
	}

	public DatabaseSession createSession() {
		DatabaseSession databaseSession = new DatabaseSession(this, keyValueStore.startTransaction());
		sessions.add(databaseSession);
		return databaseSession;
	}

	public KeyValueStore getKeyValueStore() {
		return keyValueStore;
	}

	public Set getClasses() {
		return classifiers.keyBSet();
	}

	public EClass getEClassForCid(short cid) {
		return classifiers.getB(cid);
	}

	public Short getCidOfEClass(EClass eClass) {
		return classifiers.getA(eClass);
	}

	public Registry getRegistry() {
		return registry;
	}

	public Date getCreated() {
		return created;
	}

	public void unregisterSession(DatabaseSession databaseSession) {
		sessions.remove(databaseSession);
	}

	public void setDatabaseVersion(int version, DatabaseSession databaseSession) throws BimserverLockConflictException {
		databaseSchemaVersion = version;
		registry.save(SCHEMA_VERSION, version, databaseSession);
	}

	@Override
	public Migrator getMigrator() {
		return migrator;
	}

	public boolean createTable(EClass eClass, DatabaseSession databaseSession) throws BimserverDatabaseException {
		boolean createTable = keyValueStore.createTable(eClass.getEPackage().getName() + "_" + eClass.getName(), databaseSession);
		if (createTable) {
			tableId++;
			try {
				keyValueStore.store(CLASS_LOOKUP_TABLE, BinUtils.shortToByteArray(tableId), BinUtils.stringToByteArray(eClass.getEPackage().getName() + "_" + eClass.getName()), null);
			} catch (BimserverDatabaseException e) {
				LOGGER.error("", e);
			}
			return true;
		}
		return false;
	}

	public boolean createIndexTable(EClass eClass, EStructuralFeature eStructuralFeature, DatabaseSession databaseSession) throws BimserverDatabaseException {
		boolean createTable = keyValueStore.createIndexTable(eClass.getEPackage().getName() + "_" + eClass.getName() + "_" + eStructuralFeature.getName(), databaseSession);
		return createTable;
	}

	public MetaDataManager getMetaDataManager() {
		return metaDataManager;
	}
	
	public DatabaseSession getDatabaseSession(long txnid) {
		for (DatabaseSession databaseSession : sessions) {
			if (databaseSession.getTransactionId() == txnid) {
				return databaseSession;
			}
		}
		return null;
	}

	public void incrementCommittedWrites(long committedWrites) {
		keyValueStore.incrementCommittedWrites(committedWrites);
	}

	public void incrementReads(long reads) {
		keyValueStore.incrementReads(reads);
	}
	
	public EClass getEClassForOid(long oid) throws BimserverDatabaseException {
//		ByteBuffer buffer = ByteBuffer.wrap(new byte[8]);
//		buffer.putLong(oid);
//		short cid = buffer.getShort(6);
		short cid = (short)oid;
		EClass eClass = getEClassForCid(cid);
		if (eClass == null) {
			throw new BimserverDatabaseException("No class for cid " + cid + " (cid came from oid: " + oid  + ")");
		}
		return eClass;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy