/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.runtime;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.teiid.adminapi.Model;
import org.teiid.adminapi.Model.Type;
import org.teiid.adminapi.VDB.Status;
import org.teiid.adminapi.impl.ModelMetaData;
import org.teiid.adminapi.impl.SourceMappingMetadata;
import org.teiid.adminapi.impl.VDBMetaData;
import org.teiid.core.CoreConstants;
import org.teiid.core.util.StringUtil;
import org.teiid.deployers.VDBRepository;
import org.teiid.deployers.VirtualDatabaseException;
import org.teiid.dqp.internal.datamgr.ConnectorManager;
import org.teiid.dqp.internal.datamgr.ConnectorManagerRepository;
import org.teiid.dqp.internal.process.multisource.MultiSourceElement;
import org.teiid.dqp.internal.process.multisource.MultiSourceMetadataWrapper;
import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
import org.teiid.logging.MessageLevel;
import org.teiid.metadata.Database;
import org.teiid.metadata.Datatype;
import org.teiid.metadata.MetadataException;
import org.teiid.metadata.MetadataFactory;
import org.teiid.metadata.MetadataRepository;
import org.teiid.metadata.MetadataStore;
import org.teiid.metadata.VDBResource;
import org.teiid.metadatastore.DeploymentBasedDatabaseStore;
import org.teiid.query.function.SystemFunctionManager;
import org.teiid.query.metadata.*;
import org.teiid.query.metadata.DatabaseStore.Mode;
import org.teiid.query.parser.QueryParser;
import org.teiid.translator.ExecutionFactory;
import org.teiid.translator.TranslatorException;
public abstract class AbstractVDBDeployer {
/**
* A wrapper to add a stateful text config
*/
private static class MetadataRepositoryWrapper<F, C> extends MetadataRepository<F, C> {
private MetadataRepository<F, C> repo;
private String text;
public MetadataRepositoryWrapper(MetadataRepository<F, C> repo, String text) {
this.repo = repo;
this.text = text;
}
@Override
public void loadMetadata(MetadataFactory factory,
ExecutionFactory<F, C> executionFactory, F connectionFactory) throws TranslatorException {
repo.loadMetadata(factory, executionFactory, connectionFactory, this.text);
}
};
protected ConcurrentSkipListMap<String, MetadataRepository<?, ?>> repositories = new ConcurrentSkipListMap<String, MetadataRepository<?, ?>>(String.CASE_INSENSITIVE_ORDER);
public AbstractVDBDeployer() {
repositories.put("ddl", new DDLMetadataRepository()); //$NON-NLS-1$
repositories.put("native", new NativeMetadataRepository()); //$NON-NLS-1$
repositories.put("ddl-file", new DDLFileMetadataRepository()); //$NON-NLS-1$
}
public void addMetadataRepository(String name, MetadataRepository<?, ?> metadataRepository) {
this.repositories.put(name, metadataRepository);
}
protected void assignMetadataRepositories(VDBMetaData deployment, MetadataRepository<?, ?> defaultRepo) throws VirtualDatabaseException {
for (ModelMetaData model:deployment.getModelMetaDatas().values()) {
if (model.getModelType() != Type.OTHER && (model.getName() == null || model.getName().indexOf('.') >= 0)
|| model.getName().equalsIgnoreCase(CoreConstants.SYSTEM_MODEL)
|| model.getName().equalsIgnoreCase(CoreConstants.SYSTEM_ADMIN_MODEL)
|| model.getName().equalsIgnoreCase(CoreConstants.ODBC_MODEL)) {
throw new VirtualDatabaseException(RuntimePlugin.Event.TEIID40121, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40121, model.getName(), deployment.getName(), deployment.getVersion()));
}
if (model.isSource() && model.getSourceNames().isEmpty()) {
throw new VirtualDatabaseException(RuntimePlugin.Event.TEIID40093, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40093, model.getName(), deployment.getName(), deployment.getVersion()));
}
if (model.getModelType() == Type.FUNCTION || model.getModelType() == Type.OTHER) {
continue;
}
MetadataRepository<?, ?> repo = getMetadataRepository(deployment, model, defaultRepo);
//handle multi-source column creation
if (model.isSupportsMultiSourceBindings() && Boolean.valueOf(model.getPropertyValue("multisource.addColumn"))) { //$NON-NLS-1$
List<MetadataRepository<?, ?>> repos = new ArrayList<MetadataRepository<?, ?>>(2);
repos.add(repo);
String columnName = model.getPropertyValue(MultiSourceMetadataWrapper.MULTISOURCE_COLUMN_NAME);
repos.add(new MultiSourceMetadataRepository(columnName==null?MultiSourceElement.DEFAULT_MULTI_SOURCE_ELEMENT_NAME:columnName));
repo = new ChainingMetadataRepository(repos);
}
model.addAttchment(MetadataRepository.class, repo);
}
}
private MetadataRepository<?, ?> getMetadataRepository(VDBMetaData vdb, ModelMetaData model, MetadataRepository<?, ?> defaultRepo) throws VirtualDatabaseException {
if (model.getSourceMetadataType().isEmpty()) {
if (defaultRepo != null) {
return defaultRepo;
}
if (model.isSource()) {
return new ChainingMetadataRepository(Arrays.asList(new NativeMetadataRepository(), new DirectQueryMetadataRepository()));
}
throw new VirtualDatabaseException(RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40094, model.getName(), vdb.getName(), vdb.getVersion(), null));
}
List<MetadataRepository<?, ?>> repos = new ArrayList<MetadataRepository<?,?>>(2);
for (int i = 0; i < model.getSourceMetadataType().size(); i++) {
String schemaTypes = model.getSourceMetadataType().get(i);
StringTokenizer st = new StringTokenizer(schemaTypes, ","); //$NON-NLS-1$
while (st.hasMoreTokens()) {
String repoType = st.nextToken().trim();
MetadataRepository<?, ?> current = getMetadataRepository(repoType);
if (current == null) {
throw new VirtualDatabaseException(RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40094, model.getName(), vdb.getName(), vdb.getVersion(), repoType));
}
if (model.getSourceMetadataText().size() > i) {
current = new MetadataRepositoryWrapper(current, model.getSourceMetadataText().get(i));
}
repos.add(current);
}
}
if (model.getModelType() == ModelMetaData.Type.PHYSICAL) {
repos.add(new DirectQueryMetadataRepository());
}
if (model.getModelType() == ModelMetaData.Type.VIRTUAL) {
repos.add(new MaterializationMetadataRepository());
}
if (repos.size() == 1) {
return repos.get(0);
}
return new ChainingMetadataRepository(repos);
}
protected List<ConnectorManager> getConnectorManagers(final ModelMetaData model, final ConnectorManagerRepository cmr) {
if (model.isSource()) {
Collection<SourceMappingMetadata> mappings = model.getSources().values();
List<ConnectorManager> result = new ArrayList<ConnectorManager>(mappings.size());
for (SourceMappingMetadata mapping:mappings) {
result.add(cmr.getConnectorManager(mapping.getName()));
}
return result;
}
//return a single null to give us something to loop over
return Collections.singletonList(null);
}
protected void loadMetadata(VDBMetaData vdb, ConnectorManagerRepository cmr,
MetadataStore store, VDBResources vdbResources) throws TranslatorException {
//add the system types
store.addDataTypes(SystemMetadata.getInstance().getRuntimeTypeMap());
//add domains if defined
String value = vdb.getPropertyValue(VDBMetaData.TEIID_DOMAINS);
if (value != null) {
//use a temporary store/db to retrieve the domains
DatabaseStore dbStore = new DatabaseStore() {
@Override
public Map<String, Datatype> getRuntimeTypes() {
return getVDBRepository().getRuntimeTypeMap();
}
@Override
public SystemFunctionManager getSystemFunctionManager() {
return getVDBRepository().getSystemFunctionManager();
}
};
dbStore.startEditing(true);
dbStore.databaseCreated(new Database("x", "1")); //$NON-NLS-1$ //$NON-NLS-2$
dbStore.databaseSwitched("x", "1"); //$NON-NLS-1$ //$NON-NLS-2$
dbStore.setMode(Mode.DOMAIN);
QueryParser.getQueryParser().parseDDL(dbStore, new StringReader(value));
dbStore.stopEditing();
store.addDataTypes(dbStore.getDatabase("x", "1").getMetadataStore().getDatatypes()); //$NON-NLS-1$ //$NON-NLS-2$
}
// load metadata from the models
AtomicInteger loadCount = new AtomicInteger();
for (ModelMetaData model: vdb.getModelMetaDatas().values()) {
if (model.getModelType() == Model.Type.PHYSICAL || model.getModelType() == Model.Type.VIRTUAL) {
loadCount.incrementAndGet();
}
}
if (loadCount.get() == 0) {
processVDBDDL(vdb, store, cmr, vdbResources);
getVDBRepository().finishDeployment(vdb.getName(), vdb.getVersion());
return;
}
for (ModelMetaData model: vdb.getModelMetaDatas().values()) {
MetadataRepository metadataRepository = model.getAttachment(MetadataRepository.class);
if (model.getModelType() == Model.Type.PHYSICAL || model.getModelType() == Model.Type.VIRTUAL) {
loadMetadata(vdb, model, cmr, metadataRepository, store, loadCount, vdbResources);
LogManager.logTrace(LogConstants.CTX_RUNTIME, "Model ", model.getName(), "in VDB ", vdb.getName(), " was being loaded from its repository"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
else {
LogManager.logTrace(LogConstants.CTX_RUNTIME, "Model ", model.getName(), "in VDB ", vdb.getName(), " skipped being loaded because of its type ", model.getModelType()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
}
protected abstract VDBRepository getVDBRepository();
protected abstract void loadMetadata(VDBMetaData vdb, ModelMetaData model,
ConnectorManagerRepository cmr,
MetadataRepository metadataRepository, MetadataStore store,
AtomicInteger loadCount, VDBResources vdbResources) throws TranslatorException;
protected void metadataLoaded(final VDBMetaData vdb,
final ModelMetaData model,
final MetadataStore vdbMetadataStore,
final AtomicInteger loadCount, MetadataFactory factory, boolean success, ConnectorManagerRepository cmr, VDBResources vdbResources) {
if (success) {
// merge into VDB metadata
factory.mergeInto(vdbMetadataStore);
//TODO: this is not quite correct, the source may be missing
model.clearRuntimeMessages();
model.setMetadataStatus(Model.MetadataStatus.LOADED);
} else {
model.setMetadataStatus(Model.MetadataStatus.FAILED);
vdb.setStatus(Status.FAILED);
//TODO: abort the other loads
}
if (loadCount.decrementAndGet() == 0 || vdb.getStatus() == Status.FAILED) {
if (vdb.getStatus() != Status.FAILED) {
processVDBDDL(vdb, vdbMetadataStore, cmr, vdbResources);
}
getVDBRepository().finishDeployment(vdb.getName(), vdb.getVersion());
}
}
private void processVDBDDL(final VDBMetaData vdb,
final MetadataStore vdbMetadataStore, final ConnectorManagerRepository cmr, final VDBResources vdbResources) {
if (vdb.getStatus() == Status.FAILED) {
return;
}
String ddl = vdb.getPropertyValue(VDBMetaData.TEIID_DDL);
if (ddl != null) {
final Database database = DatabaseUtil.convert(vdb, vdbMetadataStore);
CompositeMetadataStore compositeStore = new CompositeMetadataStore(vdbMetadataStore);
final TransformationMetadata metadata = new TransformationMetadata(vdb, compositeStore, null,
getVDBRepository().getSystemFunctionManager().getSystemFunctions(), null);
DeploymentBasedDatabaseStore deploymentStore = new DeploymentBasedDatabaseStore(getVDBRepository()) {
@Override
protected TransformationMetadata getTransformationMetadata() {
return metadata;
}
@Override
public void importSchema(String schemaName, String serverType,
String serverName, String foreignSchemaName,
List<String> includeTables, List<String> excludeTables,
Map<String, String> properties) {
ModelMetaData model = vdb.getModel(schemaName);
MetadataFactory factory = DatabaseStore.createMF(this, getSchema(schemaName), true);
factory.getModelProperties().putAll(model.getPropertiesMap());
factory.getModelProperties().putAll(properties);
if (!includeTables.isEmpty()) {
factory.getModelProperties().put("importer.includeTables", StringUtil.join(includeTables, ",")); //$NON-NLS-1$
}
if (!excludeTables.isEmpty()) {
factory.getModelProperties().put("importer.excludeTables", StringUtil.join(excludeTables, ",")); //$NON-NLS-1$
}
factory.setParser(new QueryParser());
if (vdbResources != null) {
factory.setVdbResources(vdbResources.getEntriesPlusVisibilities());
}
MetadataRepository baseRepo = model.getAttachment(MetadataRepository.class);
MetadataRepository metadataRepository;
try {
metadataRepository = getMetadataRepository(serverType);
if (metadataRepository == null) {
throw new VirtualDatabaseException(RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40094, model.getName(), vdb.getName(), vdb.getVersion(), serverType));
}
} catch (VirtualDatabaseException e1) {
throw new MetadataException(e1);
}
metadataRepository = new ChainingMetadataRepository(Arrays.asList(new MetadataRepositoryWrapper(metadataRepository, null), baseRepo));
ExecutionFactory ef = null;
Object cf = null;
Exception te = null;
for (ConnectorManager cm : getConnectorManagers(model, cmr)) {
if (te != null) {
LogManager.logDetail(LogConstants.CTX_RUNTIME, te, "Failed to get metadata, trying next source."); //$NON-NLS-1$
te = null;
}
try {
if (cm != null) {
ef = cm.getExecutionFactory();
cf = cm.getConnectionFactory();
}
} catch (TranslatorException e) {
LogManager.logDetail(LogConstants.CTX_RUNTIME, e, "Failed to get a connection factory for metadata load."); //$NON-NLS-1$
}
if (LogManager.isMessageToBeRecorded(LogConstants.CTX_RUNTIME, MessageLevel.TRACE)) {
LogManager.logTrace(LogConstants.CTX_RUNTIME, "CREATE SCHEMA", factory.getSchema().getName(), ";\n", DDLStringVisitor.getDDLString(factory.getSchema(), null, null)); //$NON-NLS-1$ //$NON-NLS-2$
}
try {
metadataRepository.loadMetadata(factory, ef, cf);
break;
} catch (Exception e) {
te = e;
factory = DatabaseStore.createMF(this, getSchema(schemaName), true);
factory.getModelProperties().putAll(model.getPropertiesMap());
factory.getModelProperties().putAll(properties);
factory.setParser(new QueryParser());
if (vdbResources != null) {
factory.setVdbResources(vdbResources.getEntriesPlusVisibilities());
}
}
}
if (te != null) {
if (te instanceof RuntimeException) {
throw (RuntimeException)te;
}
throw new MetadataException(te);
}
}
};
deploymentStore.startEditing(false);
deploymentStore.databaseCreated(database);
deploymentStore.databaseSwitched(database.getName(), database.getVersion());
deploymentStore.setMode(Mode.SCHEMA);
try {
QueryParser.getQueryParser().parseDDL(deploymentStore, new StringReader(ddl));
} finally {
deploymentStore.stopEditing();
}
DatabaseUtil.copyDatabaseGrantsAndRoles(database, vdb);
}
}
protected MetadataFactory createMetadataFactory(VDBMetaData vdb, MetadataStore store,
ModelMetaData model, Map<String, ? extends VDBResource> vdbResources) {
Map<String, Datatype> datatypes = store.getDatatypes();
MetadataFactory factory = new MetadataFactory(vdb.getName(), vdb.getVersion(), datatypes, model);
factory.getSchema().setPhysical(model.isSource());
factory.setParser(new QueryParser()); //for thread safety each factory gets it's own instance.
factory.setVdbResources(vdbResources);
return factory;
}
/**
* @throws VirtualDatabaseException
*/
protected MetadataRepository<?, ?> getMetadataRepository(String repoType) throws VirtualDatabaseException {
return repositories.get(repoType);
}
}