blob: 6c645f8248ed2a300ecc3b47516a4ef1532d23ff [file] [log] [blame]
/***************************************************************************
* Copyright (c) 2004 - 2008 Eike Stepper, Germany.
* 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
* Victor Roldan Betancort - http://bugs.eclipse.org/208689
**************************************************************************/
package org.eclipse.emf.cdo.server.internal.db;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.id.CDOIDUtil;
import org.eclipse.emf.cdo.common.model.CDOClass;
import org.eclipse.emf.cdo.common.model.CDOClassifierRef;
import org.eclipse.emf.cdo.common.model.CDOFeature;
import org.eclipse.emf.cdo.common.model.CDOPackage;
import org.eclipse.emf.cdo.common.model.resource.CDOResourceClass;
import org.eclipse.emf.cdo.common.model.resource.CDOResourceFolderClass;
import org.eclipse.emf.cdo.common.model.resource.CDOResourceNodeClass;
import org.eclipse.emf.cdo.common.model.resource.CDOResourcePackage;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.server.IStoreAccessor.QueryResourcesContext;
import org.eclipse.emf.cdo.server.db.IClassMapping;
import org.eclipse.emf.cdo.server.db.IDBStore;
import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
import org.eclipse.emf.cdo.server.db.IMappingStrategy;
import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
import org.eclipse.net4j.db.DBException;
import org.eclipse.net4j.db.DBUtil;
import org.eclipse.net4j.db.IDBAdapter;
import org.eclipse.net4j.db.ddl.IDBField;
import org.eclipse.net4j.db.ddl.IDBTable;
import org.eclipse.net4j.util.StringUtil;
import org.eclipse.net4j.util.collection.CloseableIterator;
import org.eclipse.net4j.util.lifecycle.Lifecycle;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Eike Stepper
*/
public abstract class MappingStrategy extends Lifecycle implements IMappingStrategy
{
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, MappingStrategy.class);
private IDBStore store;
private Map<String, String> properties;
private Map<Object, IDBTable> referenceTables = new HashMap<Object, IDBTable>();
private Map<Integer, CDOClassifierRef> classRefs = new HashMap<Integer, CDOClassifierRef>();
public MappingStrategy()
{
}
public IDBStore getStore()
{
return store;
}
public void setStore(IDBStore store)
{
this.store = store;
}
public synchronized Map<String, String> getProperties()
{
if (properties == null)
{
properties = new HashMap<String, String>();
}
return properties;
}
public synchronized void setProperties(Map<String, String> properties)
{
this.properties = properties;
}
public int getMaxTableNameLength()
{
String value = getProperties().get(PROP_MAX_TABLE_NAME_LENGTH);
return value == null ? store.getDBAdapter().getMaxTableNameLength() : Integer.valueOf(value);
}
public int getMaxFieldNameLength()
{
String value = getProperties().get(PROP_MAX_FIELD_NAME_LENGTH);
return value == null ? store.getDBAdapter().getMaxFieldNameLength() : Integer.valueOf(value);
}
public String getTableNamePrefix()
{
String value = getProperties().get(PROP_TABLE_NAME_PREFIX);
return StringUtil.safe(value);
}
public boolean isQualifiedNames()
{
String value = getProperties().get(PROP_QUALIFIED_NAMES);
return value == null ? false : Boolean.valueOf(value);
}
public boolean isForceNamesWithID()
{
String value = getProperties().get(PROP_FORCE_NAMES_WITH_ID);
return value == null ? false : Boolean.valueOf(value);
}
public ToMany getToMany()
{
String value = getProperties().get(PROP_TO_MANY_REFERENCE_MAPPING);
return value == null ? ToMany.PER_REFERENCE : ToMany.valueOf(value);
}
public ToOne getToOne()
{
String value = getProperties().get(PROP_TO_ONE_REFERENCE_MAPPING);
return value == null ? ToOne.LIKE_ATTRIBUTES : ToOne.valueOf(value);
}
public Map<Object, IDBTable> getReferenceTables()
{
return referenceTables;
}
public CDOClassifierRef getClassRef(IDBStoreAccessor accessor, int classID)
{
CDOClassifierRef classRef = classRefs.get(classID);
if (classRef == null)
{
switch (classID)
{
case ServerInfo.CDO_RESOURCE_NODE_CLASS_DBID:
{
CDOResourcePackage resourcePackage = store.getRepository().getPackageManager().getCDOResourcePackage();
classRef = resourcePackage.getCDOResourceNodeClass().createClassRef();
break;
}
case ServerInfo.CDO_RESOURCE_FOLDER_CLASS_DBID:
{
CDOResourcePackage resourcePackage = store.getRepository().getPackageManager().getCDOResourcePackage();
classRef = resourcePackage.getCDOResourceFolderClass().createClassRef();
break;
}
case ServerInfo.CDO_RESOURCE_CLASS_DBID:
{
CDOResourcePackage resourcePackage = store.getRepository().getPackageManager().getCDOResourcePackage();
classRef = resourcePackage.getCDOResourceClass().createClassRef();
break;
}
default:
classRef = accessor.readClassRef(classID);
}
classRefs.put(classID, classRef);
}
return classRef;
}
public IClassMapping getClassMapping(CDOClass cdoClass)
{
IClassMapping mapping = ClassServerInfo.getClassMapping(cdoClass);
if (mapping == NoClassMapping.INSTANCE)
{
return null;
}
if (mapping == null)
{
mapping = createClassMapping(cdoClass);
ClassServerInfo.setClassMapping(cdoClass, mapping == null ? NoClassMapping.INSTANCE : mapping);
}
return mapping;
}
public String getTableName(CDOPackage cdoPackage)
{
String name = isQualifiedNames() ? cdoPackage.getQualifiedName().replace('.', '_') : cdoPackage.getName();
return getTableName(name, "P" + PackageServerInfo.getDBID(cdoPackage));
}
public String getTableName(CDOClass cdoClass)
{
String name = isQualifiedNames() ? cdoClass.getQualifiedName().replace('.', '_') : cdoClass.getName();
return getTableName(name, "C" + ClassServerInfo.getDBID(cdoClass));
}
public String getReferenceTableName(CDOClass cdoClass, CDOFeature cdoFeature)
{
String name = isQualifiedNames() ? cdoClass.getQualifiedName().replace('.', '_') : cdoClass.getName();
name += "_";
name += cdoFeature.getName();
name += "_refs";
return getTableName(name, "F" + FeatureServerInfo.getDBID(cdoFeature));
}
public String getReferenceTableName(CDOClass cdoClass)
{
String name = isQualifiedNames() ? cdoClass.getQualifiedName().replace('.', '_') : cdoClass.getName();
name += "_refs";
return getTableName(name, "F" + ClassServerInfo.getDBID(cdoClass));
}
public String getReferenceTableName(CDOPackage cdoPackage)
{
String name = isQualifiedNames() ? cdoPackage.getQualifiedName().replace('.', '_') : cdoPackage.getName();
name += "_refs";
return getTableName(name, "F" + PackageServerInfo.getDBID(cdoPackage));
}
public String getFieldName(CDOFeature cdoFeature)
{
return getName(cdoFeature.getName(), "F" + FeatureServerInfo.getDBID(cdoFeature), getMaxFieldNameLength());
}
private String getTableName(String name, String suffix)
{
String prefix = getTableNamePrefix();
if (prefix.length() != 0 && !prefix.endsWith("_"))
{
prefix += "_";
}
return getName(prefix + name, suffix, getMaxTableNameLength());
}
private String getName(String name, String suffix, int maxLength)
{
boolean forceNamesWithID = isForceNamesWithID();
if (store.getDBAdapter().isReservedWord(name))
{
forceNamesWithID = true;
}
if (name.length() > maxLength || forceNamesWithID)
{
suffix = "_" + suffix.replace('-', 'S');
int length = Math.min(name.length(), maxLength - suffix.length());
name = name.substring(0, length) + suffix;
}
return name;
}
public String createWhereClause(long timeStamp)
{
StringBuilder builder = new StringBuilder();
if (timeStamp == CDORevision.UNSPECIFIED_DATE)
{
builder.append(CDODBSchema.ATTRIBUTES_REVISED);
builder.append("=0");
}
else
{
builder.append("(");
builder.append(CDODBSchema.ATTRIBUTES_REVISED);
builder.append("=0 OR ");
builder.append(CDODBSchema.ATTRIBUTES_REVISED);
builder.append(">=");
builder.append(timeStamp);
builder.append(") AND ");
builder.append(timeStamp);
builder.append(">=");
builder.append(CDODBSchema.ATTRIBUTES_CREATED);
}
return builder.toString();
}
public final CloseableIterator<CDOID> readObjectIDs(final IDBStoreAccessor accessor)
{
List<CDOClass> classes = getClassesWithObjectInfo();
final Iterator<CDOClass> classIt = classes.iterator();
return new ObjectIDIterator(this, accessor)
{
@Override
protected ResultSet getNextResultSet()
{
while (classIt.hasNext())
{
CDOClass cdoClass = classIt.next();
IClassMapping mapping = getClassMapping(cdoClass);
if (mapping != null)
{
IDBTable table = mapping.getTable();
if (table != null)
{
StringBuilder builder = new StringBuilder();
builder.append("SELECT DISTINCT ");
builder.append(CDODBSchema.ATTRIBUTES_ID);
builder.append(" FROM ");
builder.append(table);
String sql = builder.toString();
try
{
return accessor.getJDBCDelegate().getStatement().executeQuery(sql);
}
catch (SQLException ex)
{
throw new DBException(ex);
}
}
}
}
return null;
}
};
}
public final void queryResources(IDBStoreAccessor accessor, QueryResourcesContext context)
{
CDOID folderID = context.getFolderID();
String name = context.getName();
boolean exactMatch = context.exactMatch();
String where = createWhereClause(context.getTimeStamp());
String[] queries = getResourceQueries(folderID, name, exactMatch);
for (String query : queries)
{
StringBuilder builder = new StringBuilder();
builder.append(query);
builder.append(" AND (");
builder.append(where);
builder.append(")");
String sql = builder.toString();
if (TRACER.isEnabled())
{
TRACER.trace(sql);
}
ResultSet resultSet = null;
try
{
resultSet = accessor.getJDBCDelegate().getStatement().executeQuery(sql);
while (resultSet.next())
{
long longID = resultSet.getLong(1);
CDOID id = CDOIDUtil.createLong(longID);
if (!context.addResource(id))
{
// No more results allowed
return;
}
}
}
catch (SQLException ex)
{
throw new DBException(ex);
}
finally
{
DBUtil.close(resultSet);
}
}
}
protected abstract String[] getResourceQueries(CDOID folderID, String name, boolean exactMatch);
public void mapResourceTables(IDBAdapter dbAdapter, Connection connection)
{
CDOResourcePackage resourcePackage = store.getRepository().getPackageManager().getCDOResourcePackage();
CDOResourceNodeClass resourceNodeClass = resourcePackage.getCDOResourceNodeClass();
CDOResourceFolderClass resourceFolderClass = resourcePackage.getCDOResourceFolderClass();
CDOResourceClass resourceClass = resourcePackage.getCDOResourceClass();
PackageServerInfo.setDBID(resourcePackage, ServerInfo.CDO_RESOURCE_PACKAGE_DBID);
ClassServerInfo.setDBID(resourceNodeClass, ServerInfo.CDO_RESOURCE_NODE_CLASS_DBID);
FeatureServerInfo.setDBID(resourceNodeClass.getCDOFolderFeature(), ServerInfo.CDO_FOLDER_FEATURE_DBID);
FeatureServerInfo.setDBID(resourceNodeClass.getCDONameFeature(), ServerInfo.CDO_NAME_FEATURE_DBID);
ClassServerInfo.setDBID(resourceFolderClass, ServerInfo.CDO_RESOURCE_FOLDER_CLASS_DBID);
FeatureServerInfo.setDBID(resourceFolderClass.getCDONodesFeature(), ServerInfo.CDO_NODES_FEATURE_DBID);
ClassServerInfo.setDBID(resourceClass, ServerInfo.CDO_RESOURCE_CLASS_DBID);
FeatureServerInfo.setDBID(resourceClass.getCDOContentsFeature(), ServerInfo.CDO_CONTENTS_FEATURE_DBID);
if (dbAdapter != null && connection != null)
{
Set<IDBTable> tables = new HashSet<IDBTable>();
addResourceTables(resourceNodeClass, tables);
addResourceTables(resourceFolderClass, tables);
addResourceTables(resourceClass, tables);
if (dbAdapter.createTables(tables, connection).size() != tables.size())
{
throw new DBException("Resource tables not completely created");
}
}
}
private void addResourceTables(CDOClass cdoClass, Set<IDBTable> tables)
{
IClassMapping mapping = getClassMapping(cdoClass);
if (mapping != null)
{
Set<IDBTable> affectedTables = mapping.getAffectedTables();
tables.addAll(affectedTables);
}
}
public long repairAfterCrash(IDBAdapter dbAdapter, Connection connection)
{
long maxCDOID = 0L;
for (CDOClass idClass : getClassesWithObjectInfo())
{
IClassMapping classMapping = getClassMapping(idClass);
IDBTable table = classMapping.getTable();
IDBField idField = table.getField(CDODBSchema.ATTRIBUTES_ID);
long classMaxCDOID = DBUtil.selectMaximumLong(connection, idField);
if (TRACER.isEnabled())
{
TRACER.format("Max CDOID of table {0}: {1}", table, classMaxCDOID);
}
maxCDOID = Math.max(maxCDOID, classMaxCDOID);
}
return maxCDOID + 2L;
}
@Override
public String toString()
{
return getType();
}
/**
* The implementation of this method must take care of creating a unique ids to prevent duplicate resource paths.
*/
protected abstract IClassMapping createClassMapping(CDOClass cdoClass);
protected abstract List<CDOClass> getClassesWithObjectInfo();
@Override
protected void doBeforeActivate() throws Exception
{
super.doBeforeActivate();
checkState(store, "store");
}
}