blob: 7a9cd33b4585ad462ad4b0e980e7b702b821e229 [file] [log] [blame]
/**
* Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
* Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
*/
package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.model.CDOClassifierRef;
import org.eclipse.emf.cdo.common.protocol.CDODataInput;
import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.eresource.CDOResourceNode;
import org.eclipse.emf.cdo.eresource.EresourcePackage;
import org.eclipse.emf.cdo.server.IStoreAccessor.QueryResourcesContext;
import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
import org.eclipse.emf.cdo.server.db.IDBStore;
import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
import org.eclipse.emf.cdo.server.db.IIDHandler;
import org.eclipse.emf.cdo.server.db.mapping.IClassMapping;
import org.eclipse.emf.cdo.server.db.mapping.IListMapping;
import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
import org.eclipse.emf.cdo.server.internal.db.IObjectTypeMapper;
import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
import org.eclipse.emf.cdo.server.internal.db.mapping.AbstractMappingStrategy;
import org.eclipse.net4j.db.DBException;
import org.eclipse.net4j.db.DBUtil;
import org.eclipse.net4j.db.IDBAdapter;
import org.eclipse.net4j.db.ddl.IDBTable;
import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
import org.eclipse.net4j.util.om.monitor.OMMonitor;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import org.eclipse.emf.ecore.EClass;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection;
import java.util.List;
/**
* * This abstract base class refines {@link AbstractMappingStrategy} by implementing aspects common to horizontal
* mapping strategies -- namely:
* <ul>
* <li>object type cache (table cdo_objects)
* <li>resource query handling
* </ul>
*
* @author Eike Stepper
* @since 2.0
*/
public abstract class AbstractHorizontalMappingStrategy extends AbstractMappingStrategy
{
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractHorizontalMappingStrategy.class);
/**
* The associated object type mapper.
*/
private IObjectTypeMapper objectTypeMapper;
public CDOClassifierRef readObjectType(IDBStoreAccessor accessor, CDOID id)
{
return objectTypeMapper.getObjectType(accessor, id);
}
public void putObjectType(IDBStoreAccessor accessor, long timeStamp, CDOID id, EClass type)
{
objectTypeMapper.putObjectType(accessor, timeStamp, id, type);
}
public void repairAfterCrash(IDBAdapter dbAdapter, Connection connection)
{
IDBStore store = getStore();
if (store.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE)
{
IIDHandler idHandler = store.getIDHandler();
CDOID minLocalID = getMinLocalID(connection);
idHandler.setNextLocalObjectID(minLocalID);
CDOID maxID = objectTypeMapper.getMaxID(connection, idHandler);
idHandler.setLastObjectID(maxID);
}
}
public void queryResources(IDBStoreAccessor accessor, QueryResourcesContext context)
{
// only support timestamp in audit mode
if (context.getTimeStamp() != CDORevision.UNSPECIFIED_DATE && !hasAuditSupport())
{
throw new IllegalArgumentException("Mapping Strategy does not support audits"); //$NON-NLS-1$
}
EresourcePackage resourcesPackage = EresourcePackage.eINSTANCE;
// first query folders
IClassMapping resourceFolder = getClassMapping(resourcesPackage.getCDOResourceFolder());
boolean shallContinue = queryResources(accessor, resourceFolder, context);
// not enough results? -> query resources
if (shallContinue)
{
IClassMapping resource = getClassMapping(resourcesPackage.getCDOResource());
queryResources(accessor, resource, context);
}
}
public void queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context)
{
IIDHandler idHandler = getStore().getIDHandler();
StringBuilder builder = null;
// create a string containing "(id1,id2,...)"
// NOTE: this might not scale infinitely, because of dbms-dependent
// max size for SQL strings. But for now, it's the easiest way...
for (CDOID targetID : context.getTargetObjects().keySet())
{
// NOTE: currently no support for external references!
if (builder == null)
{
builder = new StringBuilder("(");
}
else
{
builder.append(",");
}
idHandler.appendCDOID(builder, targetID);
}
builder.append(")");
String idString = builder.toString();
for (EClass eClass : context.getSourceCandidates().keySet())
{
IClassMapping classMapping = getClassMapping(eClass);
boolean more = classMapping.queryXRefs(accessor, context, idString);
if (!more)
{
// cancel query (max results reached or user canceled)
return;
}
}
}
public void rawExport(IDBStoreAccessor accessor, CDODataOutput out, int fromBranchID, int toBranchID,
long fromCommitTime, long toCommitTime) throws IOException
{
StringBuilder builder = new StringBuilder();
builder.append(" WHERE a_t."); //$NON-NLS-1$
builder.append(CDODBSchema.ATTRIBUTES_CREATED);
builder.append(" BETWEEN "); //$NON-NLS-1$
builder.append(fromCommitTime);
builder.append(" AND "); //$NON-NLS-1$
builder.append(toCommitTime);
String attrSuffix = builder.toString();
Connection connection = accessor.getConnection();
Collection<IClassMapping> classMappings = getClassMappings(true).values();
out.writeInt(classMappings.size());
for (IClassMapping classMapping : classMappings)
{
EClass eClass = classMapping.getEClass();
out.writeCDOClassifierRef(eClass);
IDBTable table = classMapping.getDBTables().get(0);
DBUtil.serializeTable(out, connection, table, "a_t", attrSuffix);
for (IListMapping listMapping : classMapping.getListMappings())
{
rawExportList(out, connection, listMapping, table, attrSuffix);
}
}
objectTypeMapper.rawExport(connection, out, fromCommitTime, toCommitTime);
}
protected void rawExportList(CDODataOutput out, Connection connection, IListMapping listMapping, IDBTable attrTable,
String attrSuffix) throws IOException
{
for (IDBTable table : listMapping.getDBTables())
{
String listSuffix = ", " + attrTable + " a_t" + attrSuffix;
String listJoin = getListJoin("a_t", "l_t");
if (listJoin != null)
{
listSuffix += listJoin;
}
DBUtil.serializeTable(out, connection, table, "l_t", listSuffix);
}
}
public void rawImport(IDBStoreAccessor accessor, CDODataInput in, long fromCommitTime, long toCommitTime,
OMMonitor monitor) throws IOException
{
int size = in.readInt();
if (size == 0)
{
return;
}
int objectTypeMapperWork = 10;
monitor.begin(3 * size + objectTypeMapperWork);
try
{
Connection connection = accessor.getConnection();
for (int i = 0; i < size; i++)
{
EClass eClass = (EClass)in.readCDOClassifierRefAndResolve();
IClassMapping classMapping = getClassMapping(eClass);
IDBTable table = classMapping.getDBTables().get(0);
DBUtil.deserializeTable(in, connection, table, monitor.fork());
rawImportReviseOldRevisions(connection, table, monitor.fork());
rawImportUnreviseNewRevisions(connection, table, fromCommitTime, toCommitTime, monitor.fork());
List<IListMapping> listMappings = classMapping.getListMappings();
int listSize = listMappings.size();
if (listSize == 0)
{
monitor.worked();
}
else
{
OMMonitor listMonitor = monitor.fork();
listMonitor.begin(listSize);
try
{
for (IListMapping listMapping : listMappings)
{
rawImportList(in, connection, listMapping, listMonitor.fork());
}
}
finally
{
listMonitor.done();
}
}
}
objectTypeMapper.rawImport(connection, in, monitor.fork(objectTypeMapperWork));
}
finally
{
monitor.done();
}
}
protected void rawImportUnreviseNewRevisions(Connection connection, IDBTable table, long fromCommitTime,
long toCommitTime, OMMonitor monitor)
{
throw new UnsupportedOperationException("Must be overridden");
}
protected void rawImportReviseOldRevisions(Connection connection, IDBTable table, OMMonitor monitor)
{
throw new UnsupportedOperationException("Must be overridden");
}
protected void rawImportList(CDODataInput in, Connection connection, IListMapping listMapping, OMMonitor monitor)
throws IOException
{
Collection<IDBTable> tables = listMapping.getDBTables();
int size = tables.size();
if (size == 0)
{
return;
}
monitor.begin(size);
try
{
for (IDBTable table : tables)
{
DBUtil.deserializeTable(in, connection, table, monitor.fork());
}
}
finally
{
monitor.done();
}
}
public String getListJoin(String attrTable, String listTable)
{
return " AND " + attrTable + "." + CDODBSchema.ATTRIBUTES_ID + "=" + listTable + "." + CDODBSchema.LIST_REVISION_ID;
}
@Override
protected boolean isMapped(EClass eClass)
{
return !eClass.isAbstract() && !eClass.isInterface();
}
@Override
protected Collection<EClass> getClassesWithObjectInfo()
{
return getClassMappings().keySet();
}
@Override
protected void doActivate() throws Exception
{
super.doActivate();
if (objectTypeMapper == null)
{
objectTypeMapper = createObjectTypeMapper();
}
LifecycleUtil.activate(objectTypeMapper);
}
@Override
protected void doDeactivate() throws Exception
{
LifecycleUtil.deactivate(objectTypeMapper);
super.doDeactivate();
}
private IObjectTypeMapper createObjectTypeMapper()
{
ObjectTypeTable table = new ObjectTypeTable();
table.setMappingStrategy(this);
int cacheSize = getObjectTypeCacheSize();
if (cacheSize == 0)
{
return table;
}
ObjectTypeCache cache = new ObjectTypeCache(cacheSize);
cache.setMappingStrategy(this);
cache.setDelegate(table);
return cache;
}
private int getObjectTypeCacheSize()
{
int objectTypeCacheSize = ObjectTypeCache.DEFAULT_CACHE_CAPACITY;
Object value = getProperties().get(PROP_OBJECT_TYPE_CACHE_SIZE);
if (value != null)
{
try
{
int intValue = Integer.parseInt((String)value);
objectTypeCacheSize = intValue;
}
catch (NumberFormatException e)
{
OM.LOG.warn("Malformed configuration option for object type cache size. Using default.");
}
}
return objectTypeCacheSize;
}
/**
* This is an intermediate implementation. It should be changed after classmappings support a general way to implement
* queries ...
*
* @param accessor
* the accessor to use.
* @param classMapping
* the class mapping of a class instanceof {@link CDOResourceNode} which should be queried.
* @param context
* the query context containing the parameters and the result.
* @return <code>true</code> if result context is not yet full and query should continue false, if result context is
* full and query should stop.
*/
private boolean queryResources(IDBStoreAccessor accessor, IClassMapping classMapping, QueryResourcesContext context)
{
IIDHandler idHandler = getStore().getIDHandler();
PreparedStatement stmt = null;
ResultSet resultSet = null;
CDOID folderID = context.getFolderID();
String name = context.getName();
boolean exactMatch = context.exactMatch();
try
{
stmt = classMapping.createResourceQueryStatement(accessor, folderID, name, exactMatch, context);
resultSet = stmt.executeQuery();
while (resultSet.next())
{
CDOID id = idHandler.getCDOID(resultSet, 1);
if (TRACER.isEnabled())
{
TRACER.trace("Resource query returned ID " + id); //$NON-NLS-1$
}
if (!context.addResource(id))
{
// No more results allowed
return false; // don't continue
}
}
return true; // continue with other results
}
catch (SQLException ex)
{
throw new DBException(ex);
}
finally
{
DBUtil.close(resultSet);
accessor.getStatementCache().releasePreparedStatement(stmt);
}
}
private CDOID getMinLocalID(Connection connection)
{
IIDHandler idHandler = getStore().getIDHandler();
CDOID min = idHandler.getMaxCDOID();
// Do not call getClassMappings() at this point, as the package registry is not yet initialized!
String dbName = getStore().getRepository().getName();
List<String> names = DBUtil.getAllTableNames(connection, dbName);
String prefix = "SELECT MIN(t." + CDODBSchema.ATTRIBUTES_ID + ") FROM " + dbName + "." + CDODBSchema.CDO_OBJECTS
+ " AS o, " + dbName + ".";
String suffix = " AS t WHERE t." + CDODBSchema.ATTRIBUTES_BRANCH + "<0 AND t." + CDODBSchema.ATTRIBUTES_ID + "=o."
+ CDODBSchema.ATTRIBUTES_ID + " AND t." + CDODBSchema.ATTRIBUTES_CREATED + "=o."
+ CDODBSchema.ATTRIBUTES_CREATED;
for (String name : names)
{
Statement stmt = null;
ResultSet resultSet = null;
try
{
stmt = connection.createStatement();
resultSet = stmt.executeQuery(prefix + name + suffix);
if (resultSet.next())
{
CDOID id = idHandler.getCDOID(resultSet, 1);
if (id != null && idHandler.compare(id, min) < 0)
{
min = id;
}
}
}
catch (SQLException ex)
{
//$FALL-THROUGH$
}
finally
{
DBUtil.close(resultSet);
DBUtil.close(stmt);
}
}
return min;
}
}