Package org.apache.isis.objectstore.sql

Source Code of org.apache.isis.objectstore.sql.SqlObjectStore

/*
*  Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you under the Apache License, Version 2.0 (the
*  "License"); you may not use this file except in compliance
*  with the License.  You may obtain a copy of the License at
*
*        http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing,
*  software distributed under the License is distributed on an
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
*  KIND, either express or implied.  See the License for the
*  specific language governing permissions and limitations
*  under the License.
*/

package org.apache.isis.objectstore.sql;

import java.util.List;
import java.util.Vector;

import com.google.common.collect.Lists;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.isis.core.commons.debug.DebugBuilder;
import org.apache.isis.core.commons.debug.DebugString;
import org.apache.isis.core.commons.ensure.IsisAssertException;
import org.apache.isis.core.commons.exceptions.IsisException;
import org.apache.isis.core.commons.exceptions.NotYetImplementedException;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
import org.apache.isis.core.metamodel.adapter.oid.CollectionOid;
import org.apache.isis.core.metamodel.adapter.oid.Oid;
import org.apache.isis.core.metamodel.adapter.oid.RootOid;
import org.apache.isis.core.metamodel.adapter.oid.RootOidDefault;
import org.apache.isis.core.metamodel.adapter.oid.TypedOid;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.spec.SpecificationLoader;
import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
import org.apache.isis.core.runtime.persistence.objectstore.ObjectStoreSpi;
import org.apache.isis.core.runtime.persistence.objectstore.transaction.CreateObjectCommand;
import org.apache.isis.core.runtime.persistence.objectstore.transaction.DestroyObjectCommand;
import org.apache.isis.core.runtime.persistence.objectstore.transaction.PersistenceCommand;
import org.apache.isis.core.runtime.persistence.objectstore.transaction.PersistenceCommandContext;
import org.apache.isis.core.runtime.persistence.objectstore.transaction.SaveObjectCommand;
import org.apache.isis.core.runtime.persistence.query.PersistenceQueryBuiltInAbstract;
import org.apache.isis.core.runtime.persistence.query.PersistenceQueryFindAllInstances;
import org.apache.isis.core.runtime.persistence.query.PersistenceQueryFindByPattern;
import org.apache.isis.core.runtime.persistence.query.PersistenceQueryFindByTitle;
import org.apache.isis.core.runtime.system.context.IsisContext;
import org.apache.isis.core.runtime.system.persistence.PersistenceQuery;
import org.apache.isis.core.runtime.system.transaction.IsisTransactionManager;
import org.apache.isis.core.runtime.system.transaction.MessageBroker;
import org.apache.isis.core.runtime.system.transaction.UpdateNotifier;

public final class SqlObjectStore implements ObjectStoreSpi {

    private static final String TABLE_NAME = "isis_admin_services";
    // private static final String ID_COLUMN = "id";
    // private static final String PRIMARYKEY_COLUMN = "pk_id";
    public static final String BASE_NAME = "isis.persistor.sql";
    private static final Logger LOG = LoggerFactory.getLogger(SqlObjectStore.class);
    private DatabaseConnectorPool connectionPool;
    private ObjectMappingLookup objectMappingLookup;
    private boolean isInitialized;

    @Override
    public String name() {
        return "SQL Object Store";
    }

    @Override
    public void open() {
        Sql.setMetaData(connectionPool.acquire().getMetaData());

        if (!isInitialized) {
            Defaults.initialise(BASE_NAME, IsisContext.getConfiguration());
            Defaults.setPkIdLabel(Sql.identifier(Defaults.getPkIdLabel()));
            Defaults.setIdColumn(Sql.identifier(Defaults.getIdColumn()));
        }

        final DebugBuilder debug = new DebugString();
        connectionPool.debug(debug);
        LOG.info("Database: " + debug);

        objectMappingLookup.init();

        final DatabaseConnector connector = connectionPool.acquire();
        final String tableIdentifier = Sql.tableIdentifier(TABLE_NAME);
        isInitialized = connector.hasColumn(tableIdentifier, Defaults.getPkIdLabel());
        if (!isInitialized) {
            if (connector.hasTable(tableIdentifier)) {
                final StringBuffer sql = new StringBuffer();
                sql.append("drop table ");
                sql.append(tableIdentifier);
                connector.update(sql.toString());
            }
            final StringBuffer sql = new StringBuffer();
            sql.append("create table ");
            sql.append(tableIdentifier);
            sql.append(" (");
            sql.append(Defaults.getPkIdLabel());
            sql.append(" int, ");
            sql.append(Defaults.getIdColumn());
            sql.append(" varchar(255)");
            sql.append(")");
            connector.update(sql.toString());
        }
    }

    @Override
    public boolean isFixturesInstalled() {
        return isInitialized;
    }

    @Override
    public void registerService(final RootOid rootOid) {
        final DatabaseConnector connector = connectionPool.acquire();

        final StringBuffer sql = new StringBuffer();
        sql.append("insert into ");
        sql.append(Sql.tableIdentifier(TABLE_NAME));
        sql.append(" (");
        sql.append(Defaults.getPkIdLabel());
        sql.append(", ");
        sql.append(Defaults.getIdColumn());
        sql.append(") values (?,?)");

        final RootOidDefault sqlOid = (RootOidDefault) rootOid;
        connector.addToQueryValues(sqlOid.getIdentifier());
        connector.addToQueryValues(rootOid.getObjectSpecId().asString());

        connector.insert(sql.toString());
        connectionPool.release(connector);
    }

    @Override
    public void reset() {
    }

    @Override
    public void close() {
        objectMappingLookup.shutdown();
        connectionPool.shutdown();
    }

    @Override
    public void startTransaction() {
        executeSql(Defaults.START_TRANSACTION());
    }

    @Override
    public void abortTransaction() {
        executeSql(Defaults.ABORT_TRANSACTION());
    }

    @Override
    public void endTransaction() {
        executeSql(Defaults.COMMIT_TRANSACTION());
    }

    private void executeSql(String sql) {
        final DatabaseConnector connector = connectionPool.acquire();
        try {
            connector.begin();
            connector.update(sql);
            connector.commit();
            // connector.close();
        } finally {
            connectionPool.release(connector);
        }
    }

    @Override
    public CreateObjectCommand createCreateObjectCommand(final ObjectAdapter object) {
        return new CreateObjectCommand() {
            @Override
            public void execute(final PersistenceCommandContext context) {
                final DatabaseConnector connection = ((SqlExecutionContext) context).getConnection();
                LOG.debug("  create object " + object);
                final ObjectMapping mapping = objectMappingLookup.getMapping(object, connection);
                mapping.createObject(connection, object);
            }

            @Override
            public ObjectAdapter onAdapter() {
                return object;
            }

            @Override
            public String toString() {
                return "CreateObjectCommand [object=" + object + "]";
            }
        };
    }

    @Override
    public DestroyObjectCommand createDestroyObjectCommand(final ObjectAdapter object) {
        return new DestroyObjectCommand() {
            @Override
            public void execute(final PersistenceCommandContext context) {
                final DatabaseConnector connection = ((SqlExecutionContext) context).getConnection();
                LOG.debug("  destroy object " + object);
                final ObjectMapping mapping = objectMappingLookup.getMapping(object, connection);
                mapping.destroyObject(connection, object);
            }

            @Override
            public ObjectAdapter onAdapter() {
                return object;
            }

            @Override
            public String toString() {
                return "DestroyObjectCommand [object=" + object + "]";
            }
        };
    }

    @Override
    public SaveObjectCommand createSaveObjectCommand(final ObjectAdapter adapter) {
        return new SaveObjectCommand() {
            @Override
            public void execute(final PersistenceCommandContext context) {
                final DatabaseConnector connection = ((SqlExecutionContext) context).getConnection();
                LOG.debug("  save object " + adapter.toString());

                try {
                    final ObjectSpecification adapterSpec = adapter.getSpecification();
                    if (!adapterSpec.isParented()) {
                        saveRootAdapter(adapter, connection);
                    } else if (adapterSpec.isParentedOrFreeCollection()) {
                        saveParentedCollectionAdapter(adapter, connection);
                    } else {
                        throw new NotYetImplementedException("cannot yet persist aggregated objects: "
                            + adapter.toString());
                    }
                } finally {
                    connectionPool.release(connection);
                }
            }

            private void saveRootAdapter(final ObjectAdapter adapter, final DatabaseConnector connection) {
                final ObjectMapping mapping = objectMappingLookup.getMapping(adapter, connection);
                mapping.save(connection, adapter);
            }

            private void saveParentedCollectionAdapter(final ObjectAdapter collectionAdapter,
                final DatabaseConnector connection) {
                final ObjectAdapter parent = collectionAdapter.getAggregateRoot();
                LOG.debug("change to internal collection being persisted through parent");

                final Oid oid = collectionAdapter.getOid();
                final CollectionOid collectionOid = (CollectionOid) oid;
                if (!(oid instanceof CollectionOid)) {
                    throw new IsisAssertException("object should have a CollectionOid");
                }

                final ObjectMapping mapping = objectMappingLookup.getMapping(parent, connection);
                if (!mapping.saveCollection(connection, parent, collectionOid.getName())) {
                    final ObjectMapping parentMapping = objectMappingLookup.getMapping(parent, connection);
                    parentMapping.save(connection, collectionAdapter);
                }
            }

            @Override
            public ObjectAdapter onAdapter() {
                return adapter;
            }

            @Override
            public String toString() {
                return "SaveObjectCommand [object=" + adapter + "]";
            }

        };
    }

    @Override
    public void debugData(final DebugBuilder debug) {
        debug.appendln("initialised", isInitialized);
        debug.appendln("connection pool", connectionPool);
        debug.appendln("Database:");
        debug.indent();
        connectionPool.debug(debug);
        debug.unindent();
        objectMappingLookup.debugData(debug);
    }

    @Override
    public String debugTitle() {
        return null;
    }

    @Override
    public void execute(final List<PersistenceCommand> commands) {
        final DatabaseConnector connector = connectionPool.acquire();
        connector.begin();

        final IsisTransactionManager transactionManager = IsisContext.getTransactionManager();
        final MessageBroker messageBroker = IsisContext.getMessageBroker();
        final UpdateNotifier updateNotifier = IsisContext.getUpdateNotifier();
        final SqlExecutionContext context =
            new SqlExecutionContext(connector, transactionManager, messageBroker, updateNotifier);
        try {
            for (final PersistenceCommand command : commands) {
                command.execute(context);
            }
            connector.commit();
        } catch (final IsisException e) {
            LOG.warn("Failure during execution", e);
            connector.rollback();
            throw e;
        } finally {
            connectionPool.release(connector);
        }
    }

    public boolean flush(final PersistenceCommand[] commands) {
        return false;
    }

    @Override
    public List<ObjectAdapter> loadInstancesAndAdapt(final PersistenceQuery query) {
        if (query instanceof PersistenceQueryFindByTitle) {
            return findByTitle((PersistenceQueryFindByTitle) query, query.getStart(), query.getCount());
        } else if (query instanceof PersistenceQueryFindAllInstances) {
            return getAllInstances((PersistenceQueryFindAllInstances) query,  query.getStart(), query.getCount());
        } else if (query instanceof PersistenceQueryFindByPattern) {
            return findByPattern((PersistenceQueryFindByPattern) query,  query.getStart(), query.getCount());
        } else {
            throw new SqlObjectStoreException("Query type not supported: " + query);
        }
    }

    private List<ObjectAdapter> findByPattern(final PersistenceQueryFindByPattern query, final long startIndex, final long rowCount) {
        final ObjectSpecification specification = query.getSpecification();// query.getPattern().getSpecification();//
                                                                           // getSpecification();
        final DatabaseConnector connector = connectionPool.acquire();
        try {
            final List<ObjectAdapter> matchingInstances = Lists.newArrayList();

            addSpecQueryInstances(specification, connector, query, matchingInstances, startIndex, rowCount);
            return matchingInstances;

        } finally {
            connectionPool.release(connector);
        }
    }

    private void addSpecQueryInstances(final ObjectSpecification specification, final DatabaseConnector connector,
        final PersistenceQueryFindByPattern query, final List<ObjectAdapter> matchingInstances, long startIndex, long rowCount) {

        if (specification.isAbstract() == false) {
            final ObjectMapping mapper = objectMappingLookup.getMapping(specification, connector);
            final Vector<ObjectAdapter> instances = mapper.getInstances(connector, specification, query);
            matchingInstances.addAll(instances);

        }
        if (specification.hasSubclasses()) {
            final List<ObjectSpecification> subclasses = specification.subclasses();
            for (final ObjectSpecification subclassSpec : subclasses) {
                addSpecQueryInstances(subclassSpec, connector, query, matchingInstances, startIndex, rowCount);
            }
        }
    }

    private List<ObjectAdapter> getAllInstances(final PersistenceQueryBuiltInAbstract criteria, final long startIndex, final long rowCount) {
        final ObjectSpecification spec = criteria.getSpecification();
        return allInstances(spec, startIndex, rowCount);
    }

    private List<ObjectAdapter> allInstances(final ObjectSpecification spec, long startIndex, long rowCount) {
        final DatabaseConnector connector = connectionPool.acquire();
        final List<ObjectAdapter> matchingInstances = Lists.newArrayList();

        addSpecInstances(spec, connector, matchingInstances, startIndex, rowCount);

        connectionPool.release(connector);
        return matchingInstances;
    }

    private void addSpecInstances(final ObjectSpecification spec, final DatabaseConnector connector,
        final List<ObjectAdapter> matchingInstances, final long startIndex, final long rowCount) {

        if (!spec.isAbstract()) {
            final ObjectMapping mapper = objectMappingLookup.getMapping(spec, connector);
            final List<ObjectAdapter> instances = mapper.getInstances(connector, spec, startIndex, rowCount);
            matchingInstances.addAll(instances);
        }

        if (spec.hasSubclasses()) {
            final List<ObjectSpecification> subclasses = spec.subclasses();
            for (final ObjectSpecification subclassSpec : subclasses) {
                addSpecInstances(subclassSpec, connector, matchingInstances, startIndex, rowCount);
            }
        }

    }

    private List<ObjectAdapter> findByTitle(final PersistenceQueryFindByTitle criteria, final long startIndex, final long rowCount) {
        final ObjectSpecification spec = criteria.getSpecification();
        final DatabaseConnector connector = connectionPool.acquire();
        final ObjectMapping mapper = objectMappingLookup.getMapping(spec, connector);

        final Vector<ObjectAdapter> instances = mapper.getInstances(connector, spec, criteria.getTitle(), startIndex, rowCount);
        connectionPool.release(connector);

        return instances;
    }

    @Override
    public ObjectAdapter loadInstanceAndAdapt(final TypedOid oid) {
        final DatabaseConnector connection = connectionPool.acquire();
        final ObjectSpecification objectSpec = getSpecificationLookup().lookupBySpecId(oid.getObjectSpecId());
        final ObjectMapping mapper = objectMappingLookup.getMapping(objectSpec, connection);
        final ObjectAdapter object = mapper.getObject(connection, oid);
        connectionPool.release(connection);
        return object;
    }

    @Override
    public RootOid getOidForService(ObjectSpecification serviceSpec) {

        final DatabaseConnector connector = connectionPool.acquire();
        try {
            final StringBuffer sql = new StringBuffer();
            sql.append("select ");
            sql.append(Defaults.getPkIdLabel());
            sql.append(" from ");
            sql.append(Sql.tableIdentifier(TABLE_NAME));
            sql.append(" where ");
            sql.append(Defaults.getIdColumn());
            sql.append(" = ?");
            connector.addToQueryValues(serviceSpec.getSpecId().asString());

            final Results results = connector.select(sql.toString());
            if (!results.next()) {
                return null;
            }
            final int id = results.getInt(Defaults.getPkIdLabel());
            return RootOidDefault.create(serviceSpec.getSpecId(), "" + id);

        } finally {
            connectionPool.release(connector);
        }
    }

    @Override
    public boolean hasInstances(final ObjectSpecification spec) {
        final DatabaseConnector connection = connectionPool.acquire();
        final ObjectMapping mapper = objectMappingLookup.getMapping(spec, connection);
        final boolean hasInstances = mapper.hasInstances(connection, spec);
        connectionPool.release(connection);
        return hasInstances;
    }

    @Override
    public void resolveField(final ObjectAdapter object, final ObjectAssociation field) {
        if (field.isOneToManyAssociation()) {
            final DatabaseConnector connection = connectionPool.acquire();
            final ObjectSpecification spec = object.getSpecification();
            final ObjectMapping mapper = objectMappingLookup.getMapping(spec, connection);
            mapper.resolveCollection(connection, object, field);
            connectionPool.release(connection);
        } else {
            resolveImmediately(field.get(object));
        }
    }

    @Override
    public void resolveImmediately(final ObjectAdapter object) {
        final DatabaseConnector connector = connectionPool.acquire();
        final ObjectMapping mapping = objectMappingLookup.getMapping(object, connector);
        mapping.resolve(connector, object);
        connectionPool.release(connector);
    }

    // /////////////////////////////////////////////////////////
    // Dependencies (injected)
    // /////////////////////////////////////////////////////////

    public void setConnectionPool(final DatabaseConnectorPool connectionPool) {
        this.connectionPool = connectionPool;
    }

    public void setMapperLookup(final ObjectMappingLookup mapperLookup) {
        this.objectMappingLookup = mapperLookup;
    }

    // /////////////////////////////////////////////////////////
    // Dependencies (from context)
    // /////////////////////////////////////////////////////////

    protected AdapterManager getAdapterManager() {
        return IsisContext.getPersistenceSession().getAdapterManager();
    }

    protected SpecificationLoader getSpecificationLookup() {
        return IsisContext.getSpecificationLoader();
    }

}
TOP

Related Classes of org.apache.isis.objectstore.sql.SqlObjectStore

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.