/*
* 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.deployers;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.Future;
import org.teiid.adminapi.DataPolicy;
import org.teiid.adminapi.VDBImport;
import org.teiid.adminapi.impl.DataPolicyMetadata;
import org.teiid.adminapi.impl.ModelMetaData;
import org.teiid.adminapi.impl.VDBMetaData;
import org.teiid.core.CoreConstants;
import org.teiid.core.util.PropertiesUtils;
import org.teiid.dqp.internal.datamgr.ConnectorManager;
import org.teiid.dqp.internal.datamgr.ConnectorManagerRepository;
import org.teiid.dqp.internal.process.multisource.MultiSourceMetadataWrapper;
import org.teiid.metadata.FunctionMethod;
import org.teiid.metadata.MetadataStore;
import org.teiid.metadata.Schema;
import org.teiid.query.function.FunctionTree;
import org.teiid.query.function.UDFSource;
import org.teiid.query.metadata.CompositeMetadataStore;
import org.teiid.query.metadata.QueryMetadataInterface;
import org.teiid.query.metadata.TransformationMetadata;
import org.teiid.query.metadata.VDBResources;
import org.teiid.runtime.RuntimePlugin;
import org.teiid.vdb.runtime.VDBKey;
/**
* Represents the runtime state of a vdb that may aggregate several vdbs.
*/
public class CompositeVDB {
private static final boolean WIDEN_COMPARISON_TO_STRING = PropertiesUtils.getBooleanProperty(System.getProperties(), "org.teiid.widenComparisonToString", false); //$NON-NLS-1$
private VDBMetaData vdb;
private MetadataStore store;
private LinkedHashMap<String, VDBResources.Resource> visibilityMap;
private UDFMetaData udf;
LinkedHashMap<VDBKey, CompositeVDB> children;
private MetadataStore[] additionalStores;
private ConnectorManagerRepository cmr;
private FunctionTree systemFunctions;
private boolean metadataloadFinished = false;
private VDBMetaData mergedVDB;
private VDBMetaData originalVDB;
private Collection<Future<?>> tasks = Collections.synchronizedSet(new HashSet<Future<?>>());
private VDBKey vdbKey;
public CompositeVDB(VDBMetaData vdb, MetadataStore metadataStore,
LinkedHashMap<String, VDBResources.Resource> visibilityMap, UDFMetaData udf, FunctionTree systemFunctions,
ConnectorManagerRepository cmr, VDBRepository vdbRepository, MetadataStore... additionalStores)
throws VirtualDatabaseException {
this.vdb = vdb;
this.store = metadataStore;
this.visibilityMap = visibilityMap;
this.udf = udf;
this.systemFunctions = systemFunctions;
this.cmr = cmr;
this.additionalStores = additionalStores;
this.mergedVDB = vdb;
this.originalVDB = vdb;
this.vdbKey = new VDBKey(originalVDB.getName(), originalVDB.getVersion());
buildCompositeState(vdbRepository);
}
private static TransformationMetadata buildTransformationMetaData(VDBMetaData vdb,
LinkedHashMap<String, VDBResources.Resource> visibilityMap, MetadataStore store, UDFMetaData udf,
FunctionTree systemFunctions, MetadataStore[] additionalStores) {
Collection <FunctionTree> udfs = new ArrayList<FunctionTree>();
if (udf != null) {
for (Map.Entry<String, UDFSource> entry : udf.getFunctions().entrySet()) {
udfs.add(new FunctionTree(entry.getKey(), entry.getValue(), true));
}
}
//add functions for procedures
for (Schema schema:store.getSchemas().values()) {
if (!schema.getProcedures().isEmpty()) {
FunctionTree ft = FunctionTree.getFunctionProcedures(schema);
if (ft != null) {
udfs.add(ft);
}
}
}
CompositeMetadataStore compositeStore = new CompositeMetadataStore(store);
for (MetadataStore s:additionalStores) {
compositeStore.merge(s);
for (Schema schema:s.getSchemas().values()) {
if (!schema.getFunctions().isEmpty()) {
UDFSource source = new UDFSource(schema.getFunctions().values());
if (udf != null) {
source.setClassLoader(udf.getClassLoader());
}
udfs.add(new FunctionTree(schema.getName(), source, true));
}
if (!schema.getProcedures().isEmpty()) {
FunctionTree ft = FunctionTree.getFunctionProcedures(schema);
if (ft != null) {
udfs.add(ft);
}
}
}
}
TransformationMetadata metadata = new TransformationMetadata(vdb, compositeStore, visibilityMap, systemFunctions, udfs);
metadata.setUseOutputNames(false);
metadata.setWidenComparisonToString(WIDEN_COMPARISON_TO_STRING);
return metadata;
}
public VDBMetaData getVDB() {
return this.mergedVDB;
}
private void buildCompositeState(VDBRepository vdbRepository) throws VirtualDatabaseException {
if (vdb.getVDBImports().isEmpty()) {
this.vdb.addAttchment(ConnectorManagerRepository.class, this.cmr);
return;
}
VDBMetaData newMergedVDB = this.vdb.clone();
ConnectorManagerRepository mergedRepo = this.cmr;
if (!this.cmr.isShared()) {
mergedRepo = new ConnectorManagerRepository();
mergedRepo.getConnectorManagers().putAll(this.cmr.getConnectorManagers());
}
newMergedVDB.addAttchment(ConnectorManagerRepository.class, mergedRepo);
ClassLoader[] toSearch = new ClassLoader[vdb.getVDBImports().size()+1];
toSearch[0] = this.vdb.getAttachment(ClassLoader.class);
this.children = new LinkedHashMap<VDBKey, CompositeVDB>();
newMergedVDB.setImportedModels(new TreeSet<String>(String.CASE_INSENSITIVE_ORDER));
int i = 1;
for (VDBImport vdbImport : vdb.getVDBImports()) {
VDBKey key = new VDBKey(vdbImport.getName(), vdbImport.getVersion());
if (key.isAtMost()) {
//TODO: could allow partial versions
throw new VirtualDatabaseException(RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40144, vdbKey, key));
}
CompositeVDB importedVDB = vdbRepository.getCompositeVDB(key);
if (importedVDB == null) {
throw new VirtualDatabaseException(RuntimePlugin.Event.TEIID40083, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40083, vdb.getName(), vdb.getVersion(), vdbImport.getName(), vdbImport.getVersion()));
}
VDBMetaData childVDB = importedVDB.getVDB();
newMergedVDB.getVisibilityOverrides().putAll(childVDB.getVisibilityOverrides());
toSearch[i++] = childVDB.getAttachment(ClassLoader.class);
this.children.put(importedVDB.getVDBKey(), importedVDB);
if (vdbImport.isImportDataPolicies()) {
for (DataPolicy dp : importedVDB.getVDB().getDataPolicies()) {
DataPolicyMetadata role = (DataPolicyMetadata)dp;
if (newMergedVDB.addDataPolicy(role) != null) {
throw new VirtualDatabaseException(RuntimePlugin.Event.TEIID40084, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40084, vdb.getName(), vdb.getVersion(), vdbImport.getName(), vdbImport.getVersion(), role.getName()));
}
if (role.isGrantAll()) {
role.setSchemas(childVDB.getModelMetaDatas().keySet());
}
}
}
// add models
for (ModelMetaData m:childVDB.getModelMetaDatas().values()) {
if (newMergedVDB.addModel(m) != null) {
throw new VirtualDatabaseException(RuntimePlugin.Event.TEIID40085, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40085, vdb.getName(), vdb.getVersion(), vdbImport.getName(), vdbImport.getVersion(), m.getName()));
}
newMergedVDB.getImportedModels().add(m.getName());
String visibilityOverride = newMergedVDB.getPropertyValue(m.getName() + ".visible"); //$NON-NLS-1$
if (visibilityOverride != null) {
boolean visible = Boolean.valueOf(visibilityOverride);
newMergedVDB.setVisibilityOverride(m.getName(), visible);
}
}
ConnectorManagerRepository childCmr = childVDB.getAttachment(ConnectorManagerRepository.class);
if (childCmr == null) {
throw new AssertionError("childVdb does not have a connector manager repository"); //$NON-NLS-1$
}
if (!this.cmr.isShared()) {
for (Map.Entry<String, ConnectorManager> entry : childCmr.getConnectorManagers().entrySet()) {
if (mergedRepo.getConnectorManagers().put(entry.getKey(), entry.getValue()) != null) {
throw new VirtualDatabaseException(RuntimePlugin.Event.TEIID40086, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40086, vdb.getName(), vdb.getVersion(), vdbImport.getName(), vdbImport.getVersion(), entry.getKey()));
}
}
}
}
if (toSearch[0] != null) {
CombinedClassLoader ccl = new CombinedClassLoader(toSearch[0].getParent(), toSearch);
this.mergedVDB.addAttchment(ClassLoader.class, ccl);
}
this.mergedVDB = newMergedVDB;
}
private UDFMetaData getUDF() {
UDFMetaData mergedUDF = new UDFMetaData();
if (this.udf != null) {
mergedUDF.addFunctions(this.udf);
}
for (Schema schema:store.getSchemas().values()) {
Collection<FunctionMethod> funcs = schema.getFunctions().values();
mergedUDF.addFunctions(schema.getName(), funcs);
}
if (this.cmr != null) {
//system scoped common source functions
for (ConnectorManager cm:this.cmr.getConnectorManagers().values()) {
List<FunctionMethod> funcs = cm.getPushDownFunctions();
mergedUDF.addFunctions(CoreConstants.SYSTEM_MODEL, funcs);
}
}
if (this.children != null) {
//udf model functions - also scoped to the model
for (CompositeVDB child:this.children.values()) {
UDFMetaData funcs = child.getUDF();
if (funcs != null) {
mergedUDF.addFunctions(funcs);
}
}
}
return mergedUDF;
}
/**
* TODO: we are not checking for collisions here.
*/
private LinkedHashMap<String, VDBResources.Resource> getVisibilityMap() {
if (this.children == null || this.children.isEmpty()) {
return this.visibilityMap;
}
LinkedHashMap<String, VDBResources.Resource> mergedvisibilityMap = new LinkedHashMap<String, VDBResources.Resource>();
for (CompositeVDB child:this.children.values()) {
LinkedHashMap<String, VDBResources.Resource> vm = child.getVisibilityMap();
if ( vm != null) {
mergedvisibilityMap.putAll(vm);
}
}
if (this.visibilityMap != null) {
mergedvisibilityMap.putAll(this.visibilityMap);
}
return mergedvisibilityMap;
}
private MetadataStore getMetadataStore() {
return this.store;
}
VDBMetaData getOriginalVDB() {
return originalVDB;
}
public void metadataLoadFinished() {
if (this.metadataloadFinished) {
return;
}
this.metadataloadFinished = true;
MetadataStore mergedStore = getMetadataStore();
//the order of the models is important for resolving ddl
//TODO we might consider not using the intermediate MetadataStore
List<Schema> schemas = mergedStore.getSchemaList();
schemas.clear();
for (ModelMetaData model : this.vdb.getModelMetaDatas().values()) {
Schema s = mergedStore.getSchema(model.getName());
if (s != null) {
schemas.add(s);
} else {
mergedStore.getSchemas().remove(model.getName());
}
}
if (this.children != null && !this.children.isEmpty()) {
for (CompositeVDB child:this.children.values()) {
MetadataStore childStore = child.getMetadataStore();
if ( childStore != null) {
mergedStore.merge(childStore);
}
}
}
TransformationMetadata metadata = buildTransformationMetaData(mergedVDB, getVisibilityMap(), mergedStore, getUDF(), systemFunctions, this.additionalStores);
QueryMetadataInterface qmi = metadata;
Map<String, String> multiSourceModels = MultiSourceMetadataWrapper.getMultiSourceModels(mergedVDB);
if(multiSourceModels != null && !multiSourceModels.isEmpty()) {
qmi = new MultiSourceMetadataWrapper(metadata, multiSourceModels);
}
mergedVDB.addAttchment(QueryMetadataInterface.class, qmi);
mergedVDB.addAttchment(TransformationMetadata.class, metadata);
mergedVDB.addAttchment(MetadataStore.class, mergedStore);
}
LinkedHashMap<VDBKey, CompositeVDB> getChildren() {
return children;
}
public Collection<Future<?>> clearTasks() {
ArrayList<Future<?>> copy = new ArrayList<Future<?>>(tasks);
tasks.clear();
return copy;
}
public void removeTask(Future<?> future) {
tasks.remove(future);
}
public void addTask(Future<?> future) {
tasks.add(future);
}
public VDBKey getVDBKey() {
return this.vdbKey;
}
}