package nl.fontys.sofa.limo.orientdb; import com.orientechnologies.orient.core.config.OGlobalConfiguration; import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal; import com.orientechnologies.orient.core.db.record.ODatabaseRecord; import com.orientechnologies.orient.core.entity.OEntityManager; import com.orientechnologies.orient.core.metadata.schema.OClass; import com.orientechnologies.orient.core.metadata.schema.OType; import com.orientechnologies.orient.object.db.OObjectDatabaseTx; import com.orientechnologies.orient.object.metadata.OMetadataObject; import com.orientechnologies.orient.object.metadata.schema.OSchemaProxyObject; import com.orientechnologies.orient.object.serialization.OObjectSerializerContext; import com.orientechnologies.orient.object.serialization.OObjectSerializerHelper; import com.sun.jna.platform.FileUtils; import java.io.File; import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.UserPrincipal; import java.util.logging.Level; import java.util.logging.Logger; import nl.fontys.sofa.limo.domain.BaseEntity; import nl.fontys.sofa.limo.domain.component.Component; import nl.fontys.sofa.limo.domain.component.Icon; import nl.fontys.sofa.limo.domain.component.Node; import nl.fontys.sofa.limo.domain.component.event.Event; import nl.fontys.sofa.limo.domain.component.event.distribution.CauchyDistribution; import nl.fontys.sofa.limo.domain.component.event.distribution.ChiSquaredDistribution; import nl.fontys.sofa.limo.domain.component.event.distribution.DiscreteDistribution; import nl.fontys.sofa.limo.domain.component.event.distribution.Distribution; import nl.fontys.sofa.limo.domain.component.event.distribution.ExponentionalDistribution; import nl.fontys.sofa.limo.domain.component.event.distribution.FDistribution; import nl.fontys.sofa.limo.domain.component.event.distribution.GammaDistribution; import nl.fontys.sofa.limo.domain.component.event.distribution.LogNormalDistribution; import nl.fontys.sofa.limo.domain.component.event.distribution.NormalDistribution; import nl.fontys.sofa.limo.domain.component.event.distribution.PoissonDistribution; import nl.fontys.sofa.limo.domain.component.event.distribution.TriangularDistribution; import nl.fontys.sofa.limo.domain.component.event.distribution.WeibullDistribution; import nl.fontys.sofa.limo.domain.component.event.distribution.input.DoubleInputValue; import nl.fontys.sofa.limo.domain.component.event.distribution.input.InputValue; import nl.fontys.sofa.limo.domain.component.event.distribution.input.IntegerInputValue; import nl.fontys.sofa.limo.domain.component.hub.Hub; import nl.fontys.sofa.limo.domain.component.hub.Location; import nl.fontys.sofa.limo.domain.component.leg.Leg; import nl.fontys.sofa.limo.domain.component.leg.MultiModeLeg; import nl.fontys.sofa.limo.domain.component.leg.ScheduledLeg; import nl.fontys.sofa.limo.domain.component.procedure.Procedure; import nl.fontys.sofa.limo.domain.component.procedure.ProcedureCategory; import nl.fontys.sofa.limo.domain.component.procedure.value.RangeValue; import nl.fontys.sofa.limo.domain.component.procedure.value.SingleValue; import nl.fontys.sofa.limo.domain.component.procedure.value.Value; import nl.fontys.sofa.limo.domain.component.type.HubType; import nl.fontys.sofa.limo.domain.component.type.LegType; import nl.fontys.sofa.limo.domain.component.type.Type; import nl.fontys.sofa.limo.orientdb.serialization.ContinentSerializer; import nl.fontys.sofa.limo.orientdb.serialization.CountrySerializer; import nl.fontys.sofa.limo.orientdb.serialization.ExecutionStateSerializer; import nl.fontys.sofa.limo.orientdb.serialization.TimeTypeSerializer; import org.openide.modules.Places; import org.openide.util.Exceptions; /** * Singleton connection to OrientDB file database. Maintaines schema and allows * for communciation to database for the OrientDB DAOs. * * @author Dominik Kaisers {@literal <d.kaisers@student.fontys.nl>} */ public class OrientDBConnector { //<editor-fold defaultstate="collapsed" desc="SINGLETON"> /** * Singleton instance holder. */ protected static OrientDBConnector INSTANCE; /** * Get the singleton instance. Lazy loaded. * * @return Singleton instance. */ public synchronized static OrientDBConnector getInstance() { if (INSTANCE == null) { INSTANCE = new OrientDBConnector(); } return INSTANCE; } //</editor-fold> /** * Inject different database URL for test purposes. */ static String databaseURL = null; /** * Helper method for ease of use. * * @return Connection to OrientDB database. */ public static OObjectDatabaseTx connection() { OGlobalConfiguration.STORAGE_KEEP_OPEN.setValue(false); return getInstance().getConnection(); } /** * Helper method for ease of use. */ public static void close() { getInstance().closeConnection(); } protected OObjectDatabaseTx connection; protected OrientDBConnector() { } /** * Initializes everything needed for usage of OrientDB Object API if needed. */ protected void checkConnection() { // Create new conneciton if needed and set flags if (this.connection == null) { this.connection = new OObjectDatabaseTx(this.getDatabaseURL()); } // Create database if it does not exist if (!this.connection.exists()) { try { this.connection.create(); this.createSchema(); Path path = FileSystems.getDefault().getPath(Places.getUserDirectory() + File.separator + "LIMO_DB"); String username = System.getProperty("user.name"); UserPrincipal user = FileSystems.getDefault().getUserPrincipalLookupService().lookupPrincipalByName(username); Files.walk(path).forEach((Path filePath) -> { if (Files.isRegularFile(filePath)) { try { Files.setOwner(filePath, user); } catch (IOException ex) { Logger.getGlobal().log(Level.WARNING, "", ex); } } }); } catch (IOException ex) { Logger.getLogger(OrientDBConnector.class.getName()).log(Level.WARNING, "", ex); } } // Open connection if it is closed if (this.connection.isClosed()) { this.connection.open("admin", "admin"); this.createSchema(); } this.connection.setLazyLoading(false); } /** * Registers classes and serializers with the database. */ protected void createSchema() { // Register serializers OObjectSerializerContext serializer = new OObjectSerializerContext(); serializer.bind(new CountrySerializer()); serializer.bind(new ContinentSerializer()); serializer.bind(new ExecutionStateSerializer()); serializer.bind(new TimeTypeSerializer()); OObjectSerializerHelper.bindSerializerContext(null, serializer); // Register classes OEntityManager entityManager = this.connection.getEntityManager(); this.connection.setAutomaticSchemaGeneration(true); entityManager.registerEntityClass(BaseEntity.class); this.connection.setAutomaticSchemaGeneration(false); entityManager.registerEntityClass(Component.class); entityManager.registerEntityClass(Icon.class); entityManager.registerEntityClass(Node.class); entityManager.registerEntityClass(Type.class); entityManager.registerEntityClass(Event.class); entityManager.registerEntityClass(Distribution.class); entityManager.registerEntityClass(CauchyDistribution.class); entityManager.registerEntityClass(ChiSquaredDistribution.class); entityManager.registerEntityClass(DiscreteDistribution.class); entityManager.registerEntityClass(ExponentionalDistribution.class); entityManager.registerEntityClass(FDistribution.class); entityManager.registerEntityClass(GammaDistribution.class); entityManager.registerEntityClass(LogNormalDistribution.class); entityManager.registerEntityClass(NormalDistribution.class); entityManager.registerEntityClass(PoissonDistribution.class); entityManager.registerEntityClass(TriangularDistribution.class); entityManager.registerEntityClass(WeibullDistribution.class); entityManager.registerEntityClass(InputValue.class); entityManager.registerEntityClass(DoubleInputValue.class); entityManager.registerEntityClass(IntegerInputValue.class); entityManager.registerEntityClass(Hub.class); entityManager.registerEntityClass(Location.class); entityManager.registerEntityClass(Leg.class); entityManager.registerEntityClass(MultiModeLeg.class); entityManager.registerEntityClass(ScheduledLeg.class); entityManager.registerEntityClass(Procedure.class); entityManager.registerEntityClass(ProcedureCategory.class); entityManager.registerEntityClass(Value.class); entityManager.registerEntityClass(SingleValue.class); entityManager.registerEntityClass(RangeValue.class); entityManager.registerEntityClass(Type.class); entityManager.registerEntityClass(HubType.class); entityManager.registerEntityClass(LegType.class); // entityManager.registerEntityClasses("nl.fontys.sofa.limo.domain"); // Create indexes for unique identifier OMetadataObject metadata = this.connection.getMetadata(); OSchemaProxyObject schema = metadata.getSchema(); OClass clazz = schema.getClass(BaseEntity.class); if (!clazz.areIndexed("uniqueIdentifier")) { if (!clazz.existsProperty("uniqueIdentifier")) { clazz.createProperty("uniqueIdentifier", OType.STRING); } clazz.createIndex("uuid", OClass.INDEX_TYPE.UNIQUE_HASH_INDEX, "uniqueIdentifier"); } // Create class and property for value OClass iivClass = this.connection.getMetadata().getSchema().getClass(IntegerInputValue.class); if (iivClass == null) { iivClass = this.connection.getMetadata().getSchema().createClass(InputValue.class); } if (!iivClass.existsProperty("value")) { iivClass.createProperty("value", OType.INTEGER); } OClass divClass = this.connection.getMetadata().getSchema().getClass(DoubleInputValue.class); if (divClass == null) { divClass = this.connection.getMetadata().getSchema().createClass(DoubleInputValue.class); } if (!divClass.existsProperty("value")) { divClass.createProperty("value", OType.DOUBLE); } } /** * Generate the URL to the database. In this case to a folder on the hard * drive. * * @return Database URL. */ protected String getDatabaseURL() { if (databaseURL != null) { return databaseURL; } // Path path = FileSystems.getDefault().getPath(System.getProperty("user.home"), "/LIMO"); Path path = FileSystems.getDefault().getPath(Places.getUserDirectory() + File.separator + "LIMO_DB"); if (!Files.exists(path)) { try { Files.createDirectory(path); Files.setAttribute(path, "dos:hidden", true); String username = System.getProperty("user.name"); UserPrincipal user = FileSystems.getDefault().getUserPrincipalLookupService().lookupPrincipalByName(username); Files.setOwner(path, user); } catch (IOException | UnsupportedOperationException ex) { // Not on windows, whateverever Logger.getLogger(OrientDBConnector.class.getName()).log(Level.WARNING, "Error in creating database", ex); } } return "plocal:" + path.toString(); } /** * Get the instances connection object after checking that it is valid. * * @return Connection object. */ protected OObjectDatabaseTx getConnection() { this.checkConnection(); ODatabaseRecordThreadLocal.INSTANCE.set((ODatabaseRecord) this.connection.getUnderlying().getUnderlying()); return this.connection; } /** * Close the instances connection object. */ protected void closeConnection() { if (this.connection != null && !this.connection.isClosed()) { this.connection.close(); } } /** * Method to delete the database files. */ protected void deleteDatabase() { OrientDBConnector.getInstance().closeConnection(); // String url = this.getDatabaseURL(); String url = (FileSystems.getDefault().getPath(Places.getUserDirectory() + File.separator + "LIMO_DB")).toString(); System.out.println("--"); try { File dbFile = new File(url); if (dbFile.exists()) { File[] files = dbFile.listFiles(); for (File f : files) { System.out.println("Removing file:" + f.toString() + "|" + f.isFile()); f.delete(); System.out.println(f.exists()); } } dbFile.delete(); } catch (Exception e) { e.printStackTrace(); } } /** * Overkill way to clear the database. Since other methods did not work * correctly, we considered deleting the database and adding a new one. This * is called from the ClearDatabaseAction (limo-view -> actions) */ public void emptyDatabase() { this.connection.drop(); this.connection.close(); this.connection = new OObjectDatabaseTx(this.getDatabaseURL()); checkConnection(); } }