
tools.xor.service.AggregateManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xor Show documentation
Show all versions of xor Show documentation
Empowering Model Driven Architecture in J2EE applications
/**
* XOR, empowering Model Driven Architecture in J2EE applications
*
* Copyright (c) 2012, Dilip Dalton
*
* Licensed 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 tools.xor.service;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import tools.xor.AbstractType;
import tools.xor.AggregateAction;
import tools.xor.AssociationSetting;
import tools.xor.BusinessObject;
import tools.xor.DataGenerator;
import tools.xor.DefaultTypeNarrower;
import tools.xor.EntityType;
import tools.xor.ExtendedProperty;
import tools.xor.MapperSide;
import tools.xor.Settings;
import tools.xor.Type;
import tools.xor.TypeMapper;
import tools.xor.TypeNarrower;
import tools.xor.core.Interceptor;
import tools.xor.custom.AssociationStrategy;
import tools.xor.custom.DefaultAssociationStrategy;
import tools.xor.custom.DefaultDetailStrategy;
import tools.xor.custom.DetailStrategy;
import tools.xor.operation.AbstractOperation;
import tools.xor.operation.DenormalizedModifyOperation;
import tools.xor.operation.DenormalizedQueryOperation;
import tools.xor.operation.MigrateOperation;
import tools.xor.providers.jdbc.CustomPersister;
import tools.xor.providers.jdbc.JDBCDataModel;
import tools.xor.providers.jdbc.JDBCDataStore;
import tools.xor.providers.jdbc.JDBCSessionContext;
import tools.xor.service.exim.CSVExportImport;
import tools.xor.service.exim.ExcelExportImport;
import tools.xor.service.exim.ExportImport;
import tools.xor.util.ApplicationConfiguration;
import tools.xor.util.ClassUtil;
import tools.xor.util.Constants;
import tools.xor.util.ObjectCreator;
import tools.xor.util.PersistenceType;
import tools.xor.util.graph.ObjectGraph;
import tools.xor.view.AggregateViewFactory;
import tools.xor.view.Function;
import tools.xor.view.QueryFragment;
import tools.xor.view.TypeVersion;
import tools.xor.view.View;
public class AggregateManager implements Xor
{
private static final Logger logger = LogManager.getLogger(new Exception().getStackTrace()[0].getClassName());
private static final Logger owLogger = LogManager.getLogger(Constants.Log.OBJECT_WALKER);
private static final Logger sgLogger = LogManager.getLogger(Constants.Log.STATE_GRAPH);
private static int BULK_BATCH_SIZE = 1000; // Flush the session for every batch to reduce memory pressure during bulk CSV import
private DataModelFactory dataModelFactory;
private List viewFiles; // A list of files to read the view information from
private List typeVersions; // A list of the types and the versions it is valid it
private int viewVersion = TypeVersion.MIN_VERSION_VALUE;
private MetaModel metaModel; // Meta model exposed to the user
private boolean autoFlushNative; // Flag to indicate if a flush should be issued before a native query is executed
private Interceptor interceptor; // A user provided interceptor to be notified of framework events
private AssociationStrategy associationStrategy; // Determine if any extra object need to be processed
private DetailStrategy detailStrategy; // Allows the user to define a custom detail strategy
private PersistenceType persistenceType;
private TypeNarrower typeNarrower;
private String viewsDirectory;
private ForeignKeyEnhancer foreignKeyEnhancer = new DefaultForeignKeyEnhancer();
// This is maintained per thread, because the dataStore holds
// the session that is thread specific
private ThreadLocal dataStore = new ThreadLocal();
// Custom type mapper to map between differnt types.
// NOTE: The user is restricted from changing the path specified in the MetaModel.
// A truly custom solution is to provide a map between the meta model path and the custom path.
// Configured with the DefaultTypeMapper
private TypeMapper typeMapper;
private void reloadViews ()
{
// Load the default view file
(new AggregateViewFactory()).load(this);
// Load the configured view files
syncViews();
}
public AggregateManager(PersistenceType persistenceType) {
this.persistenceType = persistenceType;
}
public AggregateManager() {}
@PostConstruct
protected void init ()
{
if (associationStrategy == null) {
associationStrategy = new DefaultAssociationStrategy();
}
if (detailStrategy == null) {
detailStrategy = new DefaultDetailStrategy();
}
if (typeNarrower == null) {
typeNarrower = new DefaultTypeNarrower();
typeNarrower.setAggregateManager(this);
}
if (dataModelFactory != null) {
metaModel = new MetaModel(this);
dataModelFactory.setAggregateManager(this);
// create or make a copy if typeMapper already initialized
typeMapper = this.getDataModel().getTypeMapper();
}
else {
logger.error(
"DASFactory instance is not set on the AggregateManager instance with the default name.");
}
reloadViews();
}
public DataModelFactory getDataModelFactory ()
{
return dataModelFactory;
}
@Override
public void setDataModelFactory (DataModelFactory dasFactory)
{
this.dataModelFactory = dasFactory;
}
public MetaModel getMetaModel ()
{
return metaModel;
}
public void setMetaModel (MetaModel metaModel)
{
this.metaModel = metaModel;
}
private void syncViews ()
{
if (viewFiles == null)
return;
// Load the views
AggregateViewFactory viewFactory = new AggregateViewFactory();
for (String viewFile : viewFiles)
viewFactory.load(viewFile, this);
}
public TypeNarrower getTypeNarrower ()
{
return typeNarrower;
}
public void setTypeNarrower (TypeNarrower typeNarrower)
{
this.typeNarrower = typeNarrower;
typeNarrower.setAggregateManager(this);
}
public String getViewsDirectory ()
{
return viewsDirectory;
}
public void setViewsDirectory (String viewsDirectory)
{
this.viewsDirectory = viewsDirectory;
}
public AssociationStrategy getAssociationStrategy ()
{
return associationStrategy;
}
public void setAssociationStrategy (AssociationStrategy associationStrategy)
{
this.associationStrategy = associationStrategy;
}
public ForeignKeyEnhancer getForeignKeyEnhancer () {
return this.foreignKeyEnhancer;
}
public void setForeignKeyEnhancer (ForeignKeyEnhancer enhancer) {
this.foreignKeyEnhancer = enhancer;
}
public void setDetailStrategy (DetailStrategy detailStrategy)
{
this.detailStrategy = detailStrategy;
}
public PersistenceType getPersistenceType ()
{
return persistenceType;
}
public void setPersistenceType (PersistenceType persistenceType)
{
this.persistenceType = persistenceType;
}
public List getViewFiles ()
{
return viewFiles;
}
public void setViewFiles (List viewFiles)
{
this.viewFiles = viewFiles;
}
public List getTypeVersions ()
{
return typeVersions;
}
public void setTypeVersions (List typeVersions)
{
this.typeVersions = typeVersions;
}
public boolean isAutoFlushNative ()
{
return autoFlushNative;
}
public void setAutoFlushNative (boolean autoFlushNative)
{
this.autoFlushNative = autoFlushNative;
}
public Interceptor getInterceptor ()
{
return interceptor;
}
public void setInterceptor (Interceptor interceptor)
{
this.interceptor = interceptor;
}
public TypeMapper getTypeMapper ()
{
return typeMapper;
}
@Override
public void setTypeMapper (TypeMapper typeMapper)
{
this.typeMapper = typeMapper;
}
@Override
public DataStore getDataStore ()
{
return dataStore.get();
}
/**
* This method sets the current dataStore, irrespective
* of whether the thread had one previously. This way we do
* not have to bother about clearing an obsolete dataStore.
*
* @param po value to set
*/
public void setDataStore (
DataStore po)
{
this.dataStore.set(po);
}
@Override
public DataModel getDataModel ()
{
return dataModelFactory.create(this.typeMapper);
}
public Settings getSettings ()
{
Settings result = new Settings();
result.setAssociationStrategy(associationStrategy);
return result;
}
private class FlushHandler
{
Object oldFlushMode;
BusinessObject businessObject;
boolean existingTransaction;
Settings settings;
FlushHandler (Settings settings)
{
this.settings = settings;
oldFlushMode = getDataStore().disableAutoFlush();
if(getDataStore() instanceof JDBCDataStore) {
JDBCDataStore po = (JDBCDataStore) getDataStore();
try {
CustomPersister cp = po.getSessionContext();
if(cp.getConnection() != null && !cp.getConnection().isClosed()) {
existingTransaction = true;
}
if(!existingTransaction) {
po.getSessionContext().beginTransaction();
}
}
catch (SQLException e) {
throw ClassUtil.wrapRun(e);
}
}
}
void register (BusinessObject bo)
{
this.businessObject = bo;
}
Object instance ()
{
return businessObject.getInstance();
}
Object done ()
{
try {
return (businessObject != null) ? businessObject.getInstance() : null;
} finally {
if(oldFlushMode != null) {
getDataStore().setFlushMode(oldFlushMode);
}
if (settings.doPostFlush()) {
getDataStore().flush();
}
if(!existingTransaction && getDataStore() instanceof JDBCDataStore) {
JDBCDataStore po = (JDBCDataStore) getDataStore();
po.getSessionContext().commit();
po.getSessionContext().close();
}
}
}
}
@Override
public Transaction createTransaction(Settings settings) {
configure(settings);
if(getDataStore() instanceof JDBCDataStore) {
JDBCDataStore po = (JDBCDataStore)getDataStore();
JDBCSessionContext sc = po.getSessionContext();
return new JDBCTransaction(sc);
} else if (getDataStore() instanceof JPAPersistenceXMLPO) {
JPAPersistenceXMLPO jpaPO = (JPAPersistenceXMLPO) getDataStore();
return new JPAManualTransaction(jpaPO.getEntityManager().getTransaction());
} else if (getDataStore() instanceof JPASpringPO) {
JPASpringPO jpaPO = (JPASpringPO) getDataStore();
return new JPATransaction(jpaPO.getTxManager());
} else {
throw new UnsupportedOperationException("createTransaction is supported only on JDBC provider");
}
}
@Override
public void configure (Settings settings) {
if (getDataStore() == null) {
setDataStore(dataModelFactory.createDataStore(settings != null ? settings.getSessionContext() : null));
}
if(settings != null) {
if(settings.getDataStore() == null) {
settings.setAggregateManager(this);
settings.initDataStore(getDataStore());
if (settings.getSessionContext() != null
&& getDataStore() instanceof JDBCDataStore) {
JDBCDataStore po = ((JDBCDataStore)getDataStore());
po.getSessionContext().init((JDBCSessionContext)settings.getSessionContext());
}
} else if(settings.getDataStore() != getDataStore()) {
throw new IllegalStateException("DataStore in settings is different from AggregateManager!");
}
}
}
private void checkAndSet (Settings settings, Object inputObject)
{
Class> inputObjectClass = getEntityClass(inputObject, settings);
configure(settings);
if (settings.getAssociationStrategy() == null) {
settings.setAssociationStrategy(associationStrategy);
}
if (settings.getEntityType() == null) {
// Try to infer the type from the input object
DataModel das = getDataModel();
Class> domainClass = settings.getEntityClass();
String entityTypeName = null;
if (domainClass == null && inputObjectClass != null) {
entityTypeName = das.getTypeMapper().toDomain(inputObjectClass.getName());
if(entityTypeName != null) {
try {
domainClass = Class.forName(entityTypeName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
settings.setEntityClass(domainClass);
}
} else if(domainClass != null) {
entityTypeName = domainClass.getName();
}
// check if the view is a built-in view. The domain class can be inferred from
// the view name
if(entityTypeName == null) {
domainClass = settings.getView().inferDomainClass();
settings.setEntityClass(domainClass);
}
if (domainClass == null) {
throw new RuntimeException(
"Unable to identify the type on which to perform the operation. Need to explicitly specify the domain type.");
} else {
entityTypeName = domainClass.getName();
}
Type entityType = das.getShape().getType(entityTypeName);
if(entityType == null) {
throw new RuntimeException("EntityType needs to be provided in the settings");
}
settings.setEntityType(entityType);
settings.init(das.getShape()); // populate the view from the type if necessary
owLogger.debug("Operation on Entity Type: " + settings.getEntityType().getName());
if (owLogger.isTraceEnabled()) {
owLogger.trace(getStackTrace(10));
}
if (sgLogger.isDebugEnabled()) {
if (settings.getView().getTypeGraph((EntityType)settings.getEntityType()) != null) {
if (sgLogger.isTraceEnabled()) {
sgLogger.trace(getStackTrace(10));
}
sgLogger.debug(
"State graph of Entity: " + settings.getEntityType().getName()
+ " for view: " + settings.getView().getName());
sgLogger.debug(settings.getView().getTypeGraph((EntityType)settings.getEntityType()).dumpState());
}
}
}
if(settings.getView() != null && !settings.getView().isExpanded()) {
settings.getView().expand();
}
if (owLogger.isDebugEnabled()) {
if (settings.getExpandedAssociations() != null
&& settings.getExpandedAssociations().size() > 0) {
owLogger.debug("List of Association settings for this operation:");
for (AssociationSetting assoc : settings.getExpandedAssociations()) {
owLogger.debug(Constants.Format.INDENT_STRING + assoc.toString());
}
}
}
if (settings.doPreClear()) {
getDataStore().clear();
}
}
private String getStackTrace (int numStackElements)
{
Exception e = new Exception();
StringBuilder sb = new StringBuilder();
int skip = 2; // skip first 2
for (StackTraceElement element : e.getStackTrace()) {
if (skip-- > 0) {
continue;
}
else if (skip + numStackElements == 0) {
break;
}
sb.append(element.toString());
sb.append("\r\n");
}
return sb.toString();
}
@Override
public Object clone (Object entity, Settings settings)
{
owLogger.debug("Performing clone operation");
checkAndSet(settings, entity);
DataModel das = getDataModel();
// Not necessary as we manage the back-pointers
FlushHandler flushHandler = new FlushHandler(settings);
try {
TypeMapper typeMapper = getDataModel().getTypeMapper().newInstance(MapperSide.DOMAIN);
ObjectCreator oc = new ObjectCreator(
settings,
getDataStore(),
typeMapper);
BusinessObject from = oc.createDataObject(
entity,
oc.getShape().getType(entity.getClass()),
null,
null);
oc.setRoot(from);
BusinessObject bo = (BusinessObject)from.clone(settings);
flushHandler.register(bo);
generateVisual(bo, settings);
} finally {
flushHandler.done();
}
return flushHandler.instance();
}
private BusinessObject queryOne (Object entity, Settings settings)
{
return (BusinessObject)queryInternal(entity, settings).get(0);
}
private Class> getEntityClass (Object inputObject, Settings settings)
{
if(inputObject == null) {
if(settings.getEntityClass() != null) {
return settings.getEntityClass();
} else {
return settings.getEntityType().getInstanceClass();
}
} else if(inputObject instanceof List && ((List)inputObject).size() > 0) {
return ((List)inputObject).get(0).getClass();
} else {
return inputObject.getClass();
}
}
@Override
public Object dml(Settings settings) {
configure(settings);
AbstractOperation operation = null;
if(settings.getAction() == AggregateAction.READ) {
operation = new DenormalizedQueryOperation();
} else {
operation = new DenormalizedModifyOperation();
}
operation.execute(settings);
return operation.getResult();
}
private MapperSide findSide(Object entity, Settings settings) {
MapperSide side = MapperSide.EXTERNAL;
if ((settings.getEntityType() != null && settings.getEntityType().isOpen()) ||
getDataModel().getTypeMapper().isDomain(getEntityClass(entity, settings).getName()))
{
side = MapperSide.DOMAIN;
}
return side;
}
private List> queryInternal (Object entity, Settings settings)
{
owLogger.debug("Performing query operation");
checkAndSet(settings, entity);
if (settings.doPreFlush())
getDataStore().flush();
MapperSide side = findSide(entity, settings);
TypeMapper typeMapper = getDataModel().getTypeMapper().newInstance(side);
ObjectCreator oc = new ObjectCreator(
settings,
getDataStore(),
typeMapper);
oc.setReadOnly(true);
Type fromType = settings.getEntityType();
if (fromType == null || !fromType.isOpen()) {
fromType = oc.getType(getEntityClass(entity, settings));
if(!(fromType instanceof EntityType)) {
fromType = settings.getEntityType();
}
}
BusinessObject from = oc.createDataObject(entity, fromType, null, null);
if(from.getIdentifierValue() != null) {
settings.getParams().put(QueryFragment.ID_PARAMETER_NAME, from.getIdentifierValue());
}
// Get the narrowed class, if this is not an open type
if (!(settings.getEntityType() != null && settings.getEntityType().isOpen())) {
settings.initDowncast(getTypeNarrower(), entity, typeMapper);
}
List> dataObjects = from.query(settings);
return dataObjects;
}
public int getViewVersion ()
{
return viewVersion;
}
public void setViewVersion (int version)
{
this.viewVersion = version;
}
/**
* Convenience method to quickly get access to the view meta object
*
* @param viewName name of view
* @return AggregateView
*/
public View getView (String viewName)
{
return getDataModel().getShape().getView(viewName);
}
@Override
public Object create (Object entity, Settings settings)
{
owLogger.debug("Performing create operation");
checkAndSet(settings, entity);
// Not necessary as we manage the back-pointers
FlushHandler flushHandler = new FlushHandler(settings);
try {
MapperSide side = findSide(entity, settings);
TypeMapper typeMapper = getDataModel().getTypeMapper().newInstance(side);
ObjectCreator oc = new ObjectCreator(
settings,
getDataStore(),
typeMapper);
BusinessObject from = oc.createDataObject(
entity,
getEntityType(entity, oc, settings),
null,
null);
oc.setRoot(from);
settings.setAction(AggregateAction.CREATE);
BusinessObject bo = (BusinessObject)from.create(settings);
flushHandler.register(bo);
generateVisual(bo, settings);
} finally {
flushHandler.done();
}
return flushHandler.instance();
}
@Override
public Object toDomain (Object entity, Settings settings)
{
owLogger.debug("Performing object conversion from External to Domain");
checkAndSet(settings, entity);
TypeMapper typeMapper = getDataModel().getTypeMapper().newInstance(MapperSide.EXTERNAL);
ObjectCreator oc = new ObjectCreator(
settings,
getDataStore(),
typeMapper);
BusinessObject from = oc.createDataObject(
entity,
getEntityType(entity, oc, settings),
null,
null);
oc.setRoot(from);
BusinessObject to = (BusinessObject)from.toDomain(settings);
return to.getInstance();
}
protected Type getEntityType(Object entity, ObjectCreator oc, Settings settings) {
Type type = null;
// Collection type is used for bulk import/batching
if(entity instanceof Collection) {
type = oc.getType(entity.getClass());
} else {
type = oc.getType(entity.getClass(), settings.getEntityType());
}
return type;
}
public BusinessObject readBO (Object entity, Settings settings)
{
owLogger.debug("Performing read operation");
boolean isWrapper = false;
Object wrapper = null;
if (AbstractType.isWrapperType(entity.getClass())) {
wrapper = entity;
if (settings.getEntityClass() == null) {
throw new IllegalArgumentException("The entity class needs to be provided");
}
else {
EntityType entityType = (EntityType)getShape(settings).getType(settings.getEntityClass());
if (entityType != null) {
entity = ClassUtil.newInstance(settings.getEntityClass());
isWrapper = true;
}
else {
throw new IllegalArgumentException(
"The entity class " + settings.getEntityClass().getName() +
" does not refer to a domain class");
}
}
}
checkAndSet(settings, entity);
if (settings.doPreRefresh()) {
getDataStore().refresh(entity);
}
MapperSide side = findSide(entity, settings);
TypeMapper typeMapper = getDataModel().getTypeMapper().newInstance(side);
ObjectCreator oc = new ObjectCreator(
settings,
getDataStore(),
typeMapper);
oc.setReadOnly(true);
BusinessObject from = oc.createDataObject(
entity,
//oc.getType(entity.getClass()),
oc.getType(entity.getClass(), settings.getEntityType()),
null,
null);
if (isWrapper) {
ExtendedProperty property = (ExtendedProperty)((EntityType)from.getType()).getIdentifierProperty();
property.setValue(from, wrapper);
}
from = from.load(settings); // Get the persistent object
// perform read on it
settings.setAction(AggregateAction.READ);
BusinessObject to = (from != null) ? (BusinessObject)from.read(settings) : null;
return to;
}
private void generateVisual(BusinessObject bo, Settings settings) {
if(settings.isGenerateVisual()) {
ObjectGraph og = bo.getObjectCreator().getObjectGraph();
og.generateVisual(settings);
}
}
@Override
public Object read (Object entity, Settings settings)
{
BusinessObject to = readBO(entity, settings);
generateVisual(to, settings);
return (to != null) ? to.getNormalizedInstance(settings) : null;
}
@Override
public Object toExternal (Object entity, Settings settings)
{
owLogger.debug("Performing object conversion from Domain to External");
checkAndSet(settings, entity);
TypeMapper typeMapper = getDataModel().getTypeMapper().newInstance(MapperSide.EXTERNAL);
ObjectCreator oc = new ObjectCreator(
settings,
getDataStore(),
typeMapper);
oc.setReadOnly(true);
BusinessObject from = oc.createDataObject(
entity,
oc.getType(entity.getClass()),
null,
null);
BusinessObject to = (BusinessObject)from.toExternal(settings);
if(settings.isGenerateVisual()) {
ObjectGraph og = to.getObjectCreator().getObjectGraph();
og.generateVisual(settings);
}
return (to != null) ? to.getNormalizedInstance(settings) : null;
}
public void exportCSV(String filePath, Object inputObject, Settings settings) throws IOException
{
ExportImport exim = new CSVExportImport(this);
exim.exportAggregate(filePath, inputObject, settings);
}
@Override
public void exportAggregate (String filePath, Object inputObject, Settings settings) throws
IOException
{
ExportImport exim = new ExcelExportImport(this);
exim.exportAggregate(filePath, inputObject, settings);
}
@Override
/**
* For now we handle only one aggregate entity in the document.
* Later on we can update it handle multiple entities.
*
* Ideally, we would want each entity to be in a separate document,
* so we can process it efficiently using streaming.
*/
public Object importAggregate (String filePath, Settings settings) throws IOException
{
ExportImport exim = new ExcelExportImport(this);
return exim.importAggregate(filePath, settings);
}
@Override
public Object update (Object entity, Settings settings)
{
owLogger.debug("Performing de operation");
checkAndSet(settings, entity);
if (settings.getAction() == null) {
settings.setAction(AggregateAction.UPDATE);
} else if (settings.getAction() != AggregateAction.MERGE
&& settings.getAction() != AggregateAction.UPDATE) {
throw new IllegalStateException("The default action should either be UPDATE or MERGE");
}
// Not necessary as we manage the back-pointers
FlushHandler flushHandler = new FlushHandler(settings);
try {
MapperSide side = findSide(entity, settings);
TypeMapper typeMapper = getDataModel().getTypeMapper().newInstance(side);
ObjectCreator oc = new ObjectCreator(
settings,
getDataStore(),
typeMapper);
BusinessObject from = oc.createDataObject(
entity,
getEntityType(entity, oc, settings),
null,
null);
oc.setRoot(from);
BusinessObject bo = from.update(settings);
flushHandler.register(bo);
generateVisual(bo, settings);
} finally {
flushHandler.done();
}
return flushHandler.instance();
}
private String[] getEntitiesArray(String propertyName) {
String[] entities = new String[0];
if (ApplicationConfiguration.config().containsKey(propertyName)) {
String entitiesToMigrate = ApplicationConfiguration.config().getString(propertyName);
if (entitiesToMigrate == null || "".equals(entitiesToMigrate.trim())) {
// Nothing to migrate
return entities;
}
String[] input = entitiesToMigrate.split(",");
entities = Arrays.stream(input).map(String::trim).toArray(String[]::new);
}
return entities;
}
private String[] getEntitiesToMigrate()
{
return getEntitiesArray(Constants.Config.MIGRATE_ENTITIES);
}
private String[] getEntitiesInRelationshipMigration() {
return getEntitiesArray(Constants.Config.MIGRATE_RELATIONSHIPS);
}
@Override
public void migrate (AggregateManager source, Settings settings)
{
if (source == null) {
throw new IllegalArgumentException("Source database needs to be provided");
}
configure(settings);
MigrateOperation operation = getDataStore().getMigrateOperation(
source,
this,
null);
// Migrate just the entity including embedded objects
List orderedTypes = operation.getEntitiesInOrder(
getEntitiesToMigrate(),
settings);
Iterator iterator = orderedTypes.iterator();
while (iterator.hasNext()) {
EntityType entityType = iterator.next();
System.out.println("****** Migrating entity: " + entityType.getName());
// Create a new settings based on migrate view
Settings batchSettings = operation.build(entityType, settings);
performMigration(source, batchSettings, operation);
}
//NOTE: Specific consideration for migrating relationships
// If entity is not root concrete class then only the relationships in the
// entity is migrated
// If entity is root concreate class, then all the relationships are migrated
// This above rule helps us to avoid migrating duplicate relationships, due to
// inheritance.
// Migrate collection of embedded objects
List embeddedRelationships = operation.getEmbeddedRelationships(
getEntitiesInRelationshipMigration(),
settings);
Iterator relationshipIterator = embeddedRelationships.iterator();
while (relationshipIterator.hasNext()) {
Settings relSettings = relationshipIterator.next();
System.out.println(
"****** Migrating embedded relationship: " + relSettings.getView().getName());
performMigration(source, relSettings, operation);
}
// Migrate collection of entities
List entityRelationships = operation.getEntityRelationships(
getEntitiesInRelationshipMigration(),
settings);
relationshipIterator = entityRelationships.iterator();
while (relationshipIterator.hasNext()) {
Settings relSettings = relationshipIterator.next();
System.out.println(
"****** Migrating entity relationship: " + relSettings.getView().getName());
performMigration(source, relSettings, operation);
}
}
private void performMigration(AggregateManager source, Settings settings, MigrateOperation operation) {
settings.setPersist(true);
settings.setMainAction(AggregateAction.MIGRATE);
settings.setSessionContext(settings.getSessionContext());
// Create a new operation for each entity,so we don't mix different entities
// in the same queue
operation = getDataStore().getMigrateOperation(source, this, null);
operation.execute(settings);
}
@Override
public void delete (Object entity, Settings settings)
{
owLogger.debug("Performing delete operation");
checkAndSet(settings, entity);
// Not necessary as we manage the back-pointers
FlushHandler flushHandler = new FlushHandler(settings);
try {
TypeMapper typeMapper = getDataModel().getTypeMapper().newInstance(MapperSide.EXTERNAL);
ObjectCreator oc = new ObjectCreator(
settings,
getDataStore(),
typeMapper);
BusinessObject from = oc.createDataObject(
entity,
getEntityType(entity, oc, settings),
null,
null);
oc.setRoot(from);
// Get the persistent managed object
from = from.load(settings);
if(from != null) {
from.delete(settings);
}
} finally {
flushHandler.done();
}
}
@Override
public List patch (List entity, List snapshot, Settings settings)
{
owLogger.debug("Performing update operation");
checkAndSet(settings, entity);
settings.setBaseline(true);
if (snapshot != null && !(snapshot instanceof List)) {
throw new RuntimeException(
"snapshot should also be a list mirroring the input entity list");
}
List entityList = (List)entity;
List snapshotList = (List)snapshot;
for (int i = 0; i < entityList.size(); i++) {
attach(
entityList.get(i),
snapshot == null ? null : snapshotList.get(i),
settings);
}
// Now that the optimized persistence managed objects are in the cache
// we are ready to update them
Object updatedObjects = update(entity, settings);
if(updatedObjects instanceof List) {
return (List) updatedObjects;
} else {
List result = new ArrayList();
result.add(updatedObjects);
return result;
}
}
private Shape getShape(Settings settings) {
if(settings.getShape() == null) {
return getDataModel().getShape();
}
return settings.getShape();
}
private void attach(Object entity, Object snapshot, Settings settings) {
// attach it to the persistence layer
TypeMapper typeMapper = getDataModel().getTypeMapper().newInstance(MapperSide.EXTERNAL);
ObjectCreator oc = new ObjectCreator(
settings,
getDataStore(),
typeMapper);
BusinessObject bo = oc.createDataObject(
entity,
//entityType,
oc.getType(entity.getClass(), settings.getEntityType()),
null,
null);
BusinessObject snapshotBO = null;
if(snapshot != null) {
snapshotBO = oc.createDataObject(
snapshot,
//entityType,
oc.getType(entity.getClass(), settings.getEntityType()),
null,
null);
}
// If the object is not present in the cache then we will perform
// a short-circuit attach, i.e., avoid going to the Database
// NOTE: Not all persistence managers support this and in that case
// an exception is thrown and the update method should be used instead.
Object instance = getDataStore().getCached(
bo.getDomainType().getInstanceClass(),
bo.getIdentifierValue());
if(instance == null) {
getDataStore().attach(bo, snapshotBO, settings);
}
}
@Override
public List> query (Object entity, Settings settings)
{
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy