Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
tools.xor.service.AbstractPersistenceOrchestrator Maven / Gradle / Ivy
Go to download
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 org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.json.JSONObject;
import tools.xor.AbstractBO;
import tools.xor.AggregateAction;
import tools.xor.BusinessObject;
import tools.xor.CallInfo;
import tools.xor.EntityType;
import tools.xor.ExtendedProperty;
import tools.xor.RelationshipType;
import tools.xor.Settings;
import tools.xor.Type;
import tools.xor.TypeMapper;
import tools.xor.operation.MigrateOperation;
import tools.xor.util.AggregatePropertyPaths;
import tools.xor.util.ClassUtil;
import tools.xor.util.IntraQuery;
import tools.xor.util.ObjectCreator;
import tools.xor.util.graph.StateGraph;
import tools.xor.view.Query;
import tools.xor.view.QueryFragment;
import tools.xor.view.QueryJoinAction;
import tools.xor.view.QueryTree;
import tools.xor.view.QueryTreeInvocation;
import tools.xor.view.StoredProcedure;
import tools.xor.view.View;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public abstract class AbstractPersistenceOrchestrator implements PersistenceOrchestrator {
private static final Logger logger = LogManager.getLogger(new Exception().getStackTrace()[0].getClassName());
// Valid if the surrogate id is a global id
// if not, the type also needs to be saved
private static final String INSERT_SURROGATE_MAP_SQL = "INSERT INTO XORSURROGATEMAP"
+ "(SOURCE_ID, MIGRATED_ID) VALUES (?,?)";
public static final String QUERYJOIN_ID_INT_COL = "ID_INT";
public static final String QUERYJOIN_ID_STR_COL = "ID_STR";
public static final String QUERYJOIN_INVOC_COL = "INVOCATION_ID";
private static final String INSERT_QUERY_JOIN_SQL = "INSERT INTO %s (%s, %s, %s) VALUES (?,?,?)";
private static final String QUERY_MIGRATED_IDS = "SELECT SOURCE_ID, MIGRATED_ID FROM XORSURROGATEMAP WHERE SOURCE_ID IN (%s)";
protected abstract void createStatement (StoredProcedure sp);
@Override
public boolean supportsVersionTracking() {
return true;
}
@Override
public boolean canProcessAggregate() {
return false;
}
@Override
public void clear() {
// Overridden by subclasses
}
@Override
public void clear(Set ids) {
// Overridden by subclasses
}
@Override
public void refresh(Object object) {
// Overridden by subclasses
}
private Object getByUserKey(CallInfo callInfo) {
EntityType entityType = (EntityType)((BusinessObject) callInfo.getInput()).getPropertyType();
BusinessObject from = (BusinessObject) callInfo.getInput();
EntityType type = (EntityType)((BusinessObject) callInfo.getInput()).getType();
do {
Map param = new HashMap();
for(String key: type.getExpandedNaturalKey()) {
if(from.get(key) == null)
continue;
param.put(key, from.get(key) );
}
type = type.getParentType();
if(param.size() == 0) {
continue;
}
Object result = findByProperty(from.getDomainType(), param);
if(result != null) {
return result;
}
} // don't go above entityType
while(type != null && !type.getName().equals(entityType.getName()));
return null;
}
/**
* Give a chance to shortcircuit the persistence loading if we know for sure that the
* given object is a transient object.
* For example, we know that the identifier was generated when the object was created etc.
*
* @param from the user given object
* @return true if the given object is a transient object
*/
protected boolean isTransient(BusinessObject from) {
return false;
}
@Override
public Object getPersistentObject(CallInfo callInfo, TypeMapper typeMapper) {
if(callInfo.isBulkInput()) {
return null;
}
BusinessObject from = (BusinessObject) callInfo.getInput();
Object persistentObject = null;
if(!EntityType.class.isAssignableFrom(from.getType().getClass()))
return null;
EntityType type = (EntityType) from.getType();
if(type.isEmbedded()) // We don't separately load embedded values from the database
return null;
if(isTransient(from)) {
return null;
}
if(!(callInfo.getSettings().getAction() == AggregateAction.CLONE) )
persistentObject = getByUserKey(callInfo);
if(persistentObject == null) {
ExtendedProperty identifierProperty = (ExtendedProperty) type.getIdentifierProperty();
if(identifierProperty == null) {
logger.error("Type without identifier: " + type.getName());
} else {
Serializable id = (Serializable)identifierProperty.getValue(from);
if (id != null && !"".equals(id)) {
Class> desiredClass = typeMapper.toDomain(
type.isDomainType() ?
type.getInstanceClass() :
typeMapper.getDomainShape().getType(type.getEntityName()).getInstanceClass(), from);
persistentObject = findById(desiredClass, id);
}
}
}
return persistentObject;
}
@Override
public Object getTargetObject(BusinessObject source, String openPropertyName) {
ExtendedProperty property = (ExtendedProperty) source.getType().getProperty(openPropertyName);
if(property.getRelationshipType() == RelationshipType.TO_ONE) {
Map keyFields = property.getKeyFields();
if(keyFields.size() == 1) {
Map.Entry entry = keyFields.entrySet().iterator().next();
if( ((EntityType)property.getType()).getIdentifierProperty().getName().equals(entry.getValue()) ) {
// The value in the source object referenced by the key property
// is the id value of the target object
return findById(property.getType(), source.get(entry.getKey()));
}
} else {
// For all other cases we query the DB
Map params = new HashMap();
for(Map.Entry entry: keyFields.entrySet()) {
params.put(entry.getValue(), source.get(entry.getKey()));
}
return findByProperty(property.getType(), params);
}
}
// TODO: Handle TO_MANY
return null;
}
@Override
public Object getCached(Class> persistentClass, Object id) {
return null;
}
protected void performAttach(BusinessObject input, Object instance) {
throw new UnsupportedOperationException("The reattach operation is either not supported or not yet implemented");
}
@Override
public Object attach (BusinessObject input, BusinessObject snapshot, Settings settings)
{
Object instance = null;
View view = settings.getView();
EntityType type = (EntityType)settings.getEntityType();
if (view.getTypeGraph(type).supportsDynamicUpdate()) {
EntityType entityType = (EntityType)settings.getEntityType();
ObjectCreator oc = input.getObjectCreator();
try {
instance = AbstractBO.createInstance(
input.getObjectCreator(),
input.getIdentifierValue(),
null,
entityType,
true,
null);
if(entityType.getVersionProperty() != null ) {
((ExtendedProperty)entityType.getVersionProperty()).setValue(oc.getSettings(), instance, input.getVersionValue());
} else {
ClassUtil.initSingleLevel(input, snapshot, settings);
}
// At this point it is implementation specific, and it overridden by subclass implementations
performAttach(input, instance);
}
catch (Exception e) {
throw ClassUtil.wrapRun(e);
}
}
else {
throw new UnsupportedOperationException(
"The entity type " + settings.getEntityType().getName()
+ " does not support dynamic update for the view " + view.getName());
}
return instance;
}
@Override
public boolean supportsStoredProcedure() {
return false;
}
@Override
public EntityScroll getEntityScroll(AggregateManager source, AggregateManager target, Settings settings)
{
throw new UnsupportedOperationException("EntityScroll is not supported");
}
@Override
public MigrateOperation getMigrateOperation(AggregateManager source, AggregateManager target, Integer queueSize)
{
return new MigrateOperation(source, target, queueSize);
}
@Override
public String getOQLJoinFragment(QueryTree queryTree, IntraQuery joinEdge) {
// If this is an inheritance edge, then we need to do a downcast to the
// actual type.
String joinClause = null;
if(joinEdge.getProperty() == null) {
joinClause = getQueryCapability().getDowncastClause(queryTree, joinEdge);
} else {
joinClause = joinEdge.getNormalizedName() + " AS ";
}
return " LEFT OUTER JOIN " + joinClause + joinEdge.getEnd().getAlias();
}
@Override
public String getPolymorphicClause(Type type) {
return "";
}
protected void persistMapToTable(Connection conn, Map surrogateKeyMap) throws
SQLException
{
try(PreparedStatement ps = conn.prepareStatement(INSERT_SURROGATE_MAP_SQL)) {
for (Map.Entry entry : surrogateKeyMap.entrySet()) {
ps.setString(1, entry.getKey());
ps.setString(2, entry.getValue());
ps.addBatch();
}
ps.executeBatch();
conn.commit();
}
}
protected void saveQueryJoinTable (Connection conn, String invocationId, Set ids) throws
SQLException
{
String sql = String.format(INSERT_QUERY_JOIN_SQL,
QueryJoinAction.JOIN_TABLE_NAME,
QUERYJOIN_ID_INT_COL,
QUERYJOIN_ID_STR_COL,
QUERYJOIN_INVOC_COL);
if(ids.size() == 0) {
return;
}
// Get the type of the id object
boolean isStringType = false;
Object firstId = ids.iterator().next();
if(firstId instanceof String) {
isStringType = true;
}
try(PreparedStatement ps = conn.prepareStatement(sql)) {
for (Object id: ids) {
if(!isStringType) {
Long longValue = new Long(id.toString());
ps.setLong(1, longValue);
ps.setString(2, null);
} else {
ps.setLong(1, 0);
ps.setString(2, (String)id);
}
ps.setString(3, invocationId);
ps.addBatch();
}
ps.executeBatch();
}
}
protected Map queryMigratedIds(Connection conn, Set sourceIds) throws SQLException
{
StringBuilder idStr = new StringBuilder();
for(String id: sourceIds) {
if(idStr.length() > 0) {
idStr.append(", ");
}
idStr.append("'").append(id).append("'");
}
String sql = String.format(QUERY_MIGRATED_IDS, idStr.toString());
Map result = new HashMap<>();
if(sourceIds.size() > 0) {
try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) {
while(rs.next()) {
result.put(rs.getString(1), rs.getString(2));
}
}
}
return result;
}
@Override
public void persistSurrogateMap(Map surrogateKeyMap) {
throw new UnsupportedOperationException("Persisting of the surrogate map is not implemented");
}
@Override
public Map findMigratedSurrogateIds(Set sourceSurrogateIds) {
throw new UnsupportedOperationException("findMigratedSurrogateIds needs to have a provider implementation");
}
protected Set getSurrogateKeyPaths(Set migratePropertyPaths, EntityType entityType) {
throw new UnsupportedOperationException("Persistence provider needs to implement the method getSurrogateKeyPaths");
}
@Override
/*
* This implementation is simple because we currently only handle surrogate ids that are global. i.e., unique
* for the whole schema.
*/
public void fixRelationships(List batch, Settings settings) {
/*
* We do this by
* 1. Get the list of properties
* 2. Retrieve the value of these properties and build a map
* 3. Query the database to get the corresponding migrated values
* 4. Using the list of properties in step 1, set the new values to form the correct
* relationships
*/
// Step 1 - Get the list of surrogateKey properties
// This needs to be build using the specific provider
Set surrogateKeyPaths = getSurrogateKeyPaths(
AggregatePropertyPaths.enumerateMigrate(
settings.getEntityType()), (EntityType) settings.getEntityType());
// Step 2 - Build the map
Set sourceSurrogateIds = new HashSet<>();
for(JSONObject json: batch) {
for (String surrogateKeyPath : surrogateKeyPaths) {
Object sourceSurrogateId = StateGraph.getKeyValue(json, surrogateKeyPath);
sourceSurrogateIds.add(sourceSurrogateId.toString());
}
}
// Step 3 - Query the database and get the migratedIds given the sourceIds
Map idMap = findMigratedSurrogateIds(sourceSurrogateIds);
// Step 4 - Fix the relationships using the map
for(JSONObject json: batch) {
for (String surrogateKeyPath : surrogateKeyPaths) {
Object sourceSurrogateId = StateGraph.getKeyValue(json, surrogateKeyPath);
StateGraph.setKeyValue(json, surrogateKeyPath, idMap.get(sourceSurrogateId));
}
}
}
@Override
public void evaluateDeferred(Query query, QueryType queryType, QueryTreeInvocation qti) {
// Any subclass specific logic is overridden
qti.initInList(query);
}
@Override
public void initForQuery() {}
public boolean isManaged(Class> clazz) {
return false;
}
}