/*
* 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.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.UnknownHostException;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import javax.transaction.TransactionManager;
import javax.xml.stream.XMLStreamException;
import org.jboss.vfs.VirtualFile;
import org.teiid.PreParser;
import org.teiid.adminapi.Admin;
import org.teiid.adminapi.VDB.Status;
import org.teiid.adminapi.impl.ModelMetaData;
import org.teiid.adminapi.impl.SessionMetadata;
import org.teiid.adminapi.impl.VDBMetaData;
import org.teiid.adminapi.impl.VDBMetadataParser;
import org.teiid.adminapi.impl.VDBTranslatorMetaData;
import org.teiid.client.DQP;
import org.teiid.client.security.ILogon;
import org.teiid.client.security.InvalidSessionException;
import org.teiid.common.buffer.BufferManager;
import org.teiid.common.buffer.TupleBufferCache;
import org.teiid.core.BundleUtil.Event;
import org.teiid.core.TeiidException;
import org.teiid.core.TeiidRuntimeException;
import org.teiid.core.util.NamedThreadFactory;
import org.teiid.core.util.ObjectConverterUtil;
import org.teiid.core.util.ReflectionHelper;
import org.teiid.deployers.CompositeGlobalTableStore;
import org.teiid.deployers.CompositeVDB;
import org.teiid.deployers.ContainerLifeCycleListener;
import org.teiid.deployers.TranslatorUtil;
import org.teiid.deployers.UDFMetaData;
import org.teiid.deployers.VDBLifeCycleListener;
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.datamgr.ConnectorManagerRepository.ConnectorManagerException;
import org.teiid.dqp.internal.datamgr.ConnectorManagerRepository.ExecutionFactoryProvider;
import org.teiid.dqp.internal.datamgr.TranslatorRepository;
import org.teiid.dqp.internal.process.CachedResults;
import org.teiid.dqp.internal.process.DQPCore;
import org.teiid.dqp.internal.process.PreparedPlan;
import org.teiid.dqp.internal.process.SessionAwareCache;
import org.teiid.dqp.internal.process.TransactionServerImpl;
import org.teiid.dqp.service.BufferService;
import org.teiid.events.EventDistributor;
import org.teiid.events.EventDistributorFactory;
import org.teiid.jdbc.CallableStatementImpl;
import org.teiid.jdbc.ConnectionImpl;
import org.teiid.jdbc.LocalProfile;
import org.teiid.jdbc.PreparedStatementImpl;
import org.teiid.jdbc.TeiidDriver;
import org.teiid.jdbc.TeiidPreparedStatement;
import org.teiid.jdbc.TeiidSQLException;
import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
import org.teiid.logging.MessageLevel;
import org.teiid.metadata.MetadataFactory;
import org.teiid.metadata.MetadataRepository;
import org.teiid.metadata.MetadataStore;
import org.teiid.metadata.Schema;
import org.teiid.metadata.index.IndexMetadataRepository;
import org.teiid.metadatastore.DeploymentBasedDatabaseStore;
import org.teiid.net.ConnectionException;
import org.teiid.net.ServerConnection;
import org.teiid.net.socket.ObjectChannel;
import org.teiid.query.ObjectReplicator;
import org.teiid.query.function.SystemFunctionManager;
import org.teiid.query.metadata.DDLStringVisitor;
import org.teiid.query.metadata.PureZipFileSystem;
import org.teiid.query.metadata.TransformationMetadata;
import org.teiid.query.metadata.VDBResources;
import org.teiid.query.sql.lang.Command;
import org.teiid.query.tempdata.GlobalTableStore;
import org.teiid.query.validator.ValidatorFailure;
import org.teiid.query.validator.ValidatorReport;
import org.teiid.replication.jgroups.JGroupsObjectReplicator;
import org.teiid.services.AbstractEventDistributorFactoryService;
import org.teiid.services.BufferServiceImpl;
import org.teiid.services.SessionServiceImpl;
import org.teiid.translator.ExecutionFactory;
import org.teiid.translator.Translator;
import org.teiid.translator.TranslatorException;
import org.teiid.transport.*;
import org.teiid.vdb.runtime.VDBKey;
import org.xml.sax.SAXException;
/**
* A simplified server environment for embedded use.
*
* Needs to be started prior to use with a call to {@link #start(EmbeddedConfiguration)}
*/
@SuppressWarnings("serial")
public class EmbeddedServer extends AbstractVDBDeployer implements EventDistributorFactory, ExecutionFactoryProvider {
static {
LogManager.setLogListener(new JBossLogger());
}
private LocalProfile embeddedProfile = new LocalProfile() {
@Override
public ConnectionImpl connect(String url, Properties info)
throws TeiidSQLException {
ServerConnection conn;
try {
conn = createServerConnection(info);
} catch (TeiidException e) {
throw TeiidSQLException.create(e);
}
return new EmbeddedConnectionImpl(conn, info, url);
}
@Override
public ServerConnection createServerConnection(Properties info)
throws TeiidException {
LocalServerConnection conn = new LocalServerConnection(info, useCallingThread) {
@Override
protected ClientServiceRegistry getClientServiceRegistry(String name) {
return services;
}
@Override
public void addListener(VDBLifeCycleListener listener) {
EmbeddedServer.this.repo.addListener(listener);
}
@Override
public void removeListener(VDBLifeCycleListener listener) {
EmbeddedServer.this.repo.removeListener(listener);
}
};
conn.getWorkContext().setConnectionProfile(this);
return conn;
}
};
private final class EmbeddedConnectionImpl extends ConnectionImpl implements EmbeddedConnection {
public EmbeddedConnectionImpl(ServerConnection serverConn,
Properties info, String url) {
super(serverConn, info, url);
}
@Override
public CallableStatement prepareCall(Command command, EmbeddedRequestOptions options)
throws SQLException {
CallableStatementImpl csi = this.prepareCall(command.toString(), options.getResultSetType(), ResultSet.CONCUR_READ_ONLY);
csi.setCommand(command);
return csi;
}
@Override
public TeiidPreparedStatement prepareStatement(Command command,
EmbeddedRequestOptions options) throws SQLException {
PreparedStatementImpl psi = this.prepareStatement(command.toString(), options.getResultSetType(), ResultSet.CONCUR_READ_ONLY);
psi.setCommand(command);
return psi;
}
}
protected class ProviderAwareConnectorManagerRepository extends
ConnectorManagerRepository {
public ProviderAwareConnectorManagerRepository() {
super(true);
}
@Override
protected ConnectorManager createConnectorManager(
String translatorName, String connectionName, ExecutionFactory<Object, Object> ef) throws ConnectorManagerException {
return new ConnectorManager(translatorName, connectionName, ef) {
@Override
public Object getConnectionFactory() throws TranslatorException {
if (getConnectionName() == null) {
return null;
}
ConnectionFactoryProvider<?> connectionFactoryProvider = connectionFactoryProviders.get(getConnectionName());
if (connectionFactoryProvider != null) {
return connectionFactoryProvider.getConnectionFactory();
}
return super.getConnectionFactory();
}
};
}
}
public interface ConnectionFactoryProvider<T> {
T getConnectionFactory() throws TranslatorException;
}
public static class SimpleConnectionFactoryProvider<T> implements ConnectionFactoryProvider<T> {
private T connectionFactory;
public SimpleConnectionFactoryProvider(T connectionFactory) {
this.connectionFactory = connectionFactory;
}
@Override
public T getConnectionFactory() throws TranslatorException {
return connectionFactory;
}
}
private static class VDBValidationError extends TeiidRuntimeException {
private VDBValidationError(Event event, String message) {
super(event, message);
}
}
protected DQPCore dqp = new DQPCore();
/**
* Custom vdb repository that will immediately throw exceptions for metadata validation errors
*/
protected VDBRepository repo = new VDBRepository() {
@Override
protected boolean processMetadataValidatorReport(VDBKey key, ValidatorReport report) {
if (throwMetadataErrors) {
super.processMetadataValidatorReport(key, report); //remove
ValidatorFailure firstFailure = report.getItems().iterator().next();
throw new VDBValidationError(RuntimePlugin.Event.TEIID40095, firstFailure.getMessage());
}
return true;
}
};
class EmbeddedEventDistributorFactoryService extends AbstractEventDistributorFactoryService {
@Override
protected VDBRepository getVdbRepository() {
return repo;
}
@Override
protected ObjectReplicator getObjectReplicator() {
return replicator;
}
@Override
protected DQPCore getDQPCore() {
return dqp;
}
};
protected boolean throwMetadataErrors = true;
private ConcurrentHashMap<String, ExecutionFactory<?, ?>> translators = new ConcurrentHashMap<String, ExecutionFactory<?, ?>>();
private TranslatorRepository translatorRepository = new TranslatorRepository();
private ConcurrentHashMap<String, ConnectionFactoryProvider<?>> connectionFactoryProviders = new ConcurrentHashMap<String, ConnectionFactoryProvider<?>>();
protected SessionServiceImpl sessionService = new SessionServiceImpl();
protected ObjectReplicator replicator;
protected BufferServiceImpl bufferService = new BufferServiceImpl();
protected TransactionServerImpl transactionService = new TransactionServerImpl();
protected boolean waitForLoad;
protected ClientServiceRegistryImpl services = new ClientServiceRegistryImpl() {
@Override
public void waitForFinished(VDBKey vdbKey, int timeOutMillis) throws ConnectionException {
if (waitForLoad) {
repo.waitForFinished(vdbKey, timeOutMillis);
}
}
@Override
public ClassLoader getCallerClassloader() {
return this.getClass().getClassLoader();
};
};
protected LogonImpl logon;
private TeiidDriver driver = new TeiidDriver();
protected ConnectorManagerRepository cmr = new ProviderAwareConnectorManagerRepository();
protected AbstractEventDistributorFactoryService eventDistributorFactoryService;
protected boolean useCallingThread = true;
private Boolean running;
private EmbeddedConfiguration config;
private SessionAwareCache<CachedResults> rs;
private SessionAwareCache<PreparedPlan> ppc;
protected ArrayList<SocketListener> transports = new ArrayList<SocketListener>();
private ScheduledExecutorService scheduler;
private MaterializationManager materializationMgr = null;
private ShutDownListener shutdownListener = new ShutDownListener();
private SimpleChannelFactory channelFactory;
private NodeTracker nodeTracker = null;
public EmbeddedServer() {
}
/**
* Adds the {@link ConnectionFactoryProvider} with the given connection name to replace the default JNDI lookup strategy.
* @param name
* @param connectionFactoryProvider
* @see SimpleConnectionFactoryProvider for a basic wrapper
*/
public void addConnectionFactoryProvider(String name,
ConnectionFactoryProvider<?> connectionFactoryProvider) {
this.connectionFactoryProviders.put(name, connectionFactoryProvider);
}
/**
* Adds the object as the named connection factory to replace the default JNDI lookup strategy.
* @param name
* @param connectionFactory
*/
public void addConnectionFactory(String name, Object connectionFactory) {
this.connectionFactoryProviders.put(name, new SimpleConnectionFactoryProvider<Object>(connectionFactory));
}
public synchronized void start(@SuppressWarnings("hiding") EmbeddedConfiguration config) {
if (running != null) {
throw new IllegalStateException();
}
this.dqp.setLocalProfile(this.embeddedProfile);
this.shutdownListener.setBootInProgress(true);
this.config = config;
System.setProperty("jboss.node.name", config.getNodeName()==null?"localhost":config.getNodeName());
this.cmr.setProvider(this);
this.eventDistributorFactoryService = new EmbeddedEventDistributorFactoryService();
this.eventDistributorFactoryService.start();
this.dqp.setEventDistributor(this.eventDistributorFactoryService.getReplicatedEventDistributor());
this.scheduler = Executors.newScheduledThreadPool(config.getMaxAsyncThreads(), new NamedThreadFactory("Asynch Worker")); //$NON-NLS-1$
this.replicator = config.getObjectReplicator();
if (this.replicator == null && config.getJgroupsConfigFile() != null) {
channelFactory = new SimpleChannelFactory(config);
this.replicator = new JGroupsObjectReplicator(channelFactory, this.scheduler);
try {
this.nodeTracker = new NodeTracker(channelFactory.createChannel("teiid-node-tracker"), config.getNodeName()) {
@Override
public ScheduledExecutorService getScheduledExecutorService() {
return scheduler;
}
};
} catch (Exception e) {
LogManager.logError(LogConstants.CTX_RUNTIME, e, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40089));
}
}
this.eventDistributorFactoryService = new EmbeddedEventDistributorFactoryService();
//must be called after the replicator is set
this.eventDistributorFactoryService.start();
this.dqp.setEventDistributor(this.eventDistributorFactoryService.getReplicatedEventDistributor());
if (config.getTransactionManager() == null) {
LogManager.logInfo(LogConstants.CTX_RUNTIME, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40089));
this.transactionService.setTransactionManager((TransactionManager) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[] {TransactionManager.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
throw new UnsupportedOperationException(RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40089));
}
}));
} else {
this.transactionService.setDetectTransactions(true);
this.transactionService.setTransactionManager(config.getTransactionManager());
}
if (config.getSecurityHelper() != null) {
this.sessionService.setSecurityHelper(config.getSecurityHelper());
} else {
this.sessionService.setSecurityHelper(new DoNothingSecurityHelper());
}
if (config.getSecurityDomain() != null) {
this.sessionService.setSecurityDomain(config.getSecurityDomain());
} else {
this.sessionService.setSecurityDomain("teiid-security"); //$NON-NLS-1$
}
this.sessionService.setVDBRepository(repo);
setBufferManagerProperties(config);
BufferService bs = getBufferService();
this.dqp.setBufferManager(bs.getBufferManager());
startVDBRepository();
rs = new SessionAwareCache<CachedResults>("resultset", config.getCacheFactory(), SessionAwareCache.Type.RESULTSET, config.getMaxResultSetCacheStaleness()); //$NON-NLS-1$
ppc = new SessionAwareCache<PreparedPlan>("preparedplan", config.getCacheFactory(), SessionAwareCache.Type.PREPAREDPLAN, 0); //$NON-NLS-1$
rs.setTupleBufferCache(bs.getTupleBufferCache());
this.dqp.setResultsetCache(rs);
ppc.setTupleBufferCache(bs.getTupleBufferCache());
this.dqp.setPreparedPlanCache(ppc);
this.dqp.setTransactionService(this.transactionService);
this.dqp.start(config);
this.sessionService.setDqp(this.dqp);
this.services.setSecurityHelper(this.sessionService.getSecurityHelper());
if (this.config.getAuthenticationType() != null) {
this.services.setAuthenticationType(this.config.getAuthenticationType());
this.sessionService.setAuthenticationType(this.config.getAuthenticationType());
}
this.services.setVDBRepository(this.repo);
this.materializationMgr = getMaterializationManager();
this.repo.addListener(this.materializationMgr);
if (this.nodeTracker != null) {
this.nodeTracker.addNodeListener(this.materializationMgr);
}
this.logon = new LogonImpl(sessionService, null);
services.registerClientService(ILogon.class, logon, LogConstants.CTX_SECURITY);
services.registerClientService(DQP.class, dqp, LogConstants.CTX_DQP);
initDriver();
List<SocketConfiguration> transports = config.getTransports();
if ( transports != null && !transports.isEmpty()) {
for (SocketConfiguration socketConfig:transports) {
SocketListener socketConnection = startTransport(socketConfig, bs.getBufferManager(), config.getMaxODBCLobSizeAllowed());
if (socketConfig.getSSLConfiguration() != null) {
try {
socketConfig.getSSLConfiguration().getServerSSLEngine();
} catch (Exception e) {
throw new TeiidRuntimeException(e);
}
}
this.transports.add(socketConnection);
}
}
this.shutdownListener.setBootInProgress(false);
this.shutdownListener.started();
running = true;
}
private void setBufferManagerProperties(EmbeddedConfiguration config) {
this.bufferService.setUseDisk(config.isUseDisk());
if (config.isUseDisk()) {
if (config.getBufferDirectory() == null) {
config.setBufferDirectory(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
}
this.bufferService.setDiskDirectory(config.getBufferDirectory());
}
if(config.getProcessorBatchSize() != -1)
this.bufferService.setProcessorBatchSize(config.getProcessorBatchSize());
if(config.getMaxReserveKb() != -1)
this.bufferService.setMaxReserveKb(config.getMaxReserveKb());
if(config.getMaxProcessingKb() != -1)
this.bufferService.setMaxProcessingKb(config.getMaxProcessingKb());
this.bufferService.setInlineLobs(config.isInlineLobs());
if(config.getMaxOpenFiles() != -1)
this.bufferService.setMaxOpenFiles(config.getMaxOpenFiles());
if(config.getMaxBufferSpace() != -1)
this.bufferService.setMaxBufferSpace(config.getMaxBufferSpace());
if(config.getMaxFileSize() != -1)
this.bufferService.setMaxFileSize(config.getMaxFileSize());
this.bufferService.setEncryptFiles(config.isEncryptFiles());
if(config.getMaxStorageObjectSize() != -1) {
this.bufferService.setMaxStorageObjectSize(config.getMaxStorageObjectSize());
}
this.bufferService.setMemoryBufferOffHeap(config.isMemoryBufferOffHeap());
if(config.getMemoryBufferSpace() != -1)
this.bufferService.setMemoryBufferSpace(config.getMemoryBufferSpace());
}
private void initDriver() {
driver.setLocalProfile(embeddedProfile);
}
private SocketListener startTransport(SocketConfiguration socketConfig, BufferManager bm, int maxODBCLobSize) {
InetSocketAddress address = null;
try {
address = new InetSocketAddress(socketConfig.getResolvedHostAddress(), socketConfig.getPortNumber());
} catch (UnknownHostException e) {
throw new TeiidRuntimeException(RuntimePlugin.Event.TEIID40065, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40065));
}
if (socketConfig.getProtocol() == WireProtocol.teiid) {
return new SocketListener(address, socketConfig, this.services, bm) {
@Override
public ChannelListener createChannelListener(ObjectChannel channel) {
//TODO: this is a little dirty, but allows us to inject the appropriate connection profile
SocketClientInstance instance = (SocketClientInstance) super.createChannelListener(channel);
instance.getWorkContext().setConnectionProfile(embeddedProfile);
return instance;
}
};
}
else if (socketConfig.getProtocol() == WireProtocol.pg) {
ODBCSocketListener odbc = new ODBCSocketListener(address, socketConfig, this.services, bm, maxODBCLobSize, this.logon, driver);
return odbc;
}
throw new AssertionError("Unknown protocol " + socketConfig.getProtocol()); //$NON-NLS-1$
}
private void startVDBRepository() {
this.repo.addListener(new VDBLifeCycleListener() {
@Override
public void added(String name, CompositeVDB vdb) {
}
@Override
public void removed(String name, CompositeVDB vdb) {
if (replicator != null) {
replicator.stop(vdb.getVDB().getAttachment(GlobalTableStore.class));
}
rs.clearForVDB(vdb.getVDBKey());
ppc.clearForVDB(vdb.getVDBKey());
for (SessionMetadata session : sessionService.getSessionsLoggedInToVDB(vdb.getVDBKey())) {
try {
sessionService.closeSession(session.getSessionId());
} catch (InvalidSessionException e) {
}
}
}
@Override
public void finishedDeployment(String name, CompositeVDB vdb) {
if (!vdb.getVDB().getStatus().equals(Status.ACTIVE)) {
return;
}
GlobalTableStore gts = CompositeGlobalTableStore.createInstance(vdb, dqp.getBufferManager(), replicator);
vdb.getVDB().addAttchment(GlobalTableStore.class, gts);
}
@Override
public void beforeRemove(String name, CompositeVDB vdb) {
}
});
this.repo.setSystemFunctionManager(new SystemFunctionManager());
this.repo.start();
}
protected BufferService getBufferService() {
bufferService.start();
if (replicator != null) {
try {
final TupleBufferCache tbc = replicator.replicate("$BM$", TupleBufferCache.class, bufferService.getBufferManager(), 0); //$NON-NLS-1$
return new BufferService() {
@Override
public BufferManager getBufferManager() {
return bufferService.getBufferManager();
}
@Override
public TupleBufferCache getTupleBufferCache() {
return tbc;
}
};
} catch (Exception e) {
throw new TeiidRuntimeException(e);
}
}
return bufferService;
}
protected MaterializationManager getMaterializationManager() {
return new MaterializationManager(this.shutdownListener) {
@Override
public ScheduledExecutorService getScheduledExecutorService() {
return scheduler;
}
@Override
public ScheduledExecutorService getExecutor() {
return scheduler;
}
@Override
public DQPCore getDQP() {
return dqp;
}
@Override
public VDBRepository getVDBRepository() {
return repo;
}
};
}
/**
* Adds a definition of the {@link ExecutionFactory} using the default name either from the {@link Translator} annotation or the class name.
* Only {@link ExecutionFactory} classes with a {@link Translator} annotation can be referenced by {@link #addTranslator(String, String, Map)}
* @param ef
* @throws TranslatorException
*/
public void addTranslator(Class<? extends ExecutionFactory> clazz) throws TranslatorException {
try {
VDBTranslatorMetaData vdbTranslatorMetaData = TranslatorUtil.buildTranslatorMetadata(clazz.newInstance(), null);
if (vdbTranslatorMetaData != null) {
translatorRepository.addTranslatorMetadata(vdbTranslatorMetaData.getName(), vdbTranslatorMetaData);
} else {
//not a well defined translator
ExecutionFactory<?, ?> instance = clazz.newInstance();
instance.start();
addTranslator(clazz.getName(), instance);
}
} catch (InstantiationException | IllegalAccessException e) {
throw new TranslatorException(e);
}
}
/**
* Add an override translator
* @param name
* @param type the name of an existing translator to override
* @param properties
*/
public void addTranslator(String name, String type, Map<String, String> properties) throws TranslatorException {
VDBTranslatorMetaData vdbTranslatorMetaData = new VDBTranslatorMetaData();
vdbTranslatorMetaData.setName(name);
VDBTranslatorMetaData parent = translatorRepository.getTranslatorMetaData(type);
if (parent == null) {
throw new TranslatorException(RuntimePlugin.Event.TEIID40136, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40136, type));
}
vdbTranslatorMetaData.setParent(parent);
if (properties != null) {
Properties p = new Properties();
p.putAll(properties);
vdbTranslatorMetaData.setProperties(p);
}
this.translatorRepository.addTranslatorMetadata(name, vdbTranslatorMetaData);
}
/**
* Add a named {@link ExecutionFactory}. NOTE: Only this single instance will be shared for all usage.
* @param name
* @param ef the already started ExecutionFactory
* @see {@link #addTranslator(String, String, Map)} or {@link #addTranslator(Class)}
*/
public void addTranslator(String name, ExecutionFactory<?, ?> ef) {
translators.put(name, ef);
VDBTranslatorMetaData vdbTranslatorMetaData = TranslatorUtil.buildTranslatorMetadata(ef, null);
if (vdbTranslatorMetaData != null) {
this.translatorRepository.addTranslatorMetadata(name, vdbTranslatorMetaData);
}
}
/**
* Deploy the given set of models as vdb name.1
* @param name
* @param models
* @throws ConnectorManagerException
* @throws VirtualDatabaseException
* @throws TranslatorException
*/
public void deployVDB(String name, ModelMetaData... models)
throws ConnectorManagerException, VirtualDatabaseException, TranslatorException {
VDBMetaData vdb = new VDBMetaData();
vdb.setXmlDeployment(true);
VDBKey key = new VDBKey(name, null);
vdb.setName(key.getName());
if (key.isAtMost()) {
if (name.endsWith(".")) {
//error
} else {
vdb.setVersion("1"); //$NON-NLS-1$
}
} else {
vdb.setVersion(key.getVersion());
}
vdb.setModels(Arrays.asList(models));
//TODO: the api should be hardened to prevent the creation of invalid metadata
//missing source/translator names will cause issues
deployVDB(vdb, null);
}
/**
* Deploy a vdb.xml file. The name and version will be derived from the xml.
* @param is, which will be closed by this deployment
* @throws TranslatorException
* @throws ConnectorManagerException
* @throws VirtualDatabaseException
* @throws IOException
*/
public void deployVDB(InputStream is) throws VirtualDatabaseException, ConnectorManagerException, TranslatorException, IOException {
deployVDB(is, false);
}
/**
* Deploy a vdb.xml file. The name and version will be derived from the xml.
* @param is, which will be closed by this deployment
* @param ddl, true if the file contents are DDL
* @throws TranslatorException
* @throws ConnectorManagerException
* @throws VirtualDatabaseException
* @throws IOException
*/
public void deployVDB(InputStream is, boolean ddl) throws VirtualDatabaseException, ConnectorManagerException, TranslatorException, IOException {
if (is == null) {
return;
}
byte[] bytes = ObjectConverterUtil.convertToByteArray(is);
VDBMetaData metadata = null;
if (ddl) {
DeploymentBasedDatabaseStore store = new DeploymentBasedDatabaseStore(getVDBRepository());
metadata = store.getVDBMetadata(new String(bytes));
} else {
try {
//TODO: find a way to do this off of the stream
VDBMetadataParser.validate(new ByteArrayInputStream(bytes));
} catch (SAXException e) {
throw new VirtualDatabaseException(e);
}
try {
metadata = VDBMetadataParser.unmarshell(new ByteArrayInputStream(bytes));
} catch (XMLStreamException e) {
throw new VirtualDatabaseException(e);
}
}
metadata.setXmlDeployment(true);
deployVDB(metadata, null);
}
/**
* Deploy a vdb zip file. The name and version will be derived from the xml.
* @param url
* @throws TranslatorException
* @throws ConnectorManagerException
* @throws VirtualDatabaseException
* @throws URISyntaxException
* @throws IOException
*/
public void deployVDBZip(URL url) throws VirtualDatabaseException, ConnectorManagerException, TranslatorException, IOException, URISyntaxException {
VirtualFile root = PureZipFileSystem.mount(url);
VDBMetaData metadata;
VirtualFile vdbMetadata = root.getChild("/META-INF/vdb.xml"); //$NON-NLS-1$
if (vdbMetadata.exists()) {
try {
VDBMetadataParser.validate(vdbMetadata.openStream());
} catch (SAXException e) {
throw new VirtualDatabaseException(e);
}
InputStream is = vdbMetadata.openStream();
try {
metadata = VDBMetadataParser.unmarshell(is);
} catch (XMLStreamException e) {
throw new VirtualDatabaseException(e);
}
} else {
vdbMetadata = root.getChild("/META-INF/vdb.ddl"); //$NON-NLS-1$
DeploymentBasedDatabaseStore store = new DeploymentBasedDatabaseStore(getVDBRepository());
metadata = store.getVDBMetadata(ObjectConverterUtil.convertToString(vdbMetadata.openStream()));
}
VDBResources resources = new VDBResources(root, metadata);
deployVDB(metadata, resources);
}
protected boolean allowOverrideTranslators() {
return false;
}
protected void deployVDB(VDBMetaData vdb, VDBResources resources)
throws ConnectorManagerException, VirtualDatabaseException, TranslatorException {
checkStarted();
if (!vdb.getOverrideTranslators().isEmpty() && !allowOverrideTranslators()) {
throw new VirtualDatabaseException(RuntimePlugin.Event.TEIID40106, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40106, vdb.getName()));
}
vdb.addAttchment(ClassLoader.class, Thread.currentThread().getContextClassLoader());
try {
createPreParser(vdb);
} catch (TeiidException e1) {
throw new VirtualDatabaseException(e1);
}
cmr.createConnectorManagers(vdb, this);
MetadataStore metadataStore = new MetadataStore();
UDFMetaData udfMetaData = new UDFMetaData();
udfMetaData.setFunctionClassLoader(Thread.currentThread().getContextClassLoader());
MetadataRepository<?, ?> defaultRepo = null;
LinkedHashMap<String, VDBResources.Resource> visibilityMap = null;
if (resources != null) {
//check to see if there is an index file. if there is then we assume
//that index is the default metadata repo
for (String s : resources.getEntriesPlusVisibilities().keySet()) {
if (s.endsWith(VDBResources.INDEX_EXT)) {
defaultRepo = new IndexMetadataRepository();
break;
}
}
visibilityMap = resources.getEntriesPlusVisibilities();
} else {
visibilityMap = new LinkedHashMap<String, VDBResources.Resource>();
}
this.assignMetadataRepositories(vdb, defaultRepo);
repo.addVDB(vdb, metadataStore, visibilityMap, udfMetaData, cmr);
try {
this.loadMetadata(vdb, cmr, metadataStore, resources);
} catch (VDBValidationError e) {
throw new VirtualDatabaseException(RuntimePlugin.Event.valueOf(e.getCode()), e.getMessage());
}
}
@Override
protected MetadataRepository<?, ?> getMetadataRepository(String repoType)
throws VirtualDatabaseException {
if ("index".equals(repoType)) { //$NON-NLS-1$
//return a new instance since the repos are globally scoped
//this does not support MMX style index files organized by type
return new IndexMetadataRepository();
}
return super.getMetadataRepository(repoType);
}
/**
* TODO: consolidate this logic more into the abstract deployer
*/
@Override
protected void loadMetadata(VDBMetaData vdb, ModelMetaData model,
ConnectorManagerRepository cmr,
MetadataRepository metadataRepository, MetadataStore store,
AtomicInteger loadCount, VDBResources vdbResources) throws TranslatorException {
MetadataFactory factory = createMetadataFactory(vdb, store, model, vdbResources==null?Collections.EMPTY_MAP:vdbResources.getEntriesPlusVisibilities());
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 = createMetadataFactory(vdb, store, model, vdbResources==null?Collections.EMPTY_MAP:vdbResources.getEntriesPlusVisibilities());
}
}
if (te != null) {
if (te instanceof TranslatorException) {
throw (TranslatorException)te;
}
if (te instanceof RuntimeException) {
throw (RuntimeException)te;
}
throw new TranslatorException(te);
}
metadataLoaded(vdb, model, store, loadCount, factory, true, cmr, vdbResources);
}
public void undeployVDB(String vdbName) {
checkStarted();
this.repo.removeVDB(vdbName, "1"); //$NON-NLS-1$
}
EmbeddedConfiguration getConfiguration() {
return this.config;
}
/**
* Stops the server. Once stopped it cannot be restarted.
*/
public synchronized void stop() {
if (running == null || !running) {
return;
}
if (this.channelFactory != null) {
this.channelFactory.stop();
}
this.shutdownListener.setShutdownInProgress(true);
this.repo.removeListener(this.materializationMgr);
this.scheduler.shutdownNow();
for (SocketListener socket:this.transports) {
socket.stop();
}
this.sessionService.stop();
this.transports.clear();
dqp.stop();
if (config != null) {
config.stop();
}
eventDistributorFactoryService.stop();
config.getCacheFactory().destroy();
config.setCacheFactory(null);
if (this.bufferService != null) {
this.bufferService.stop();
}
bufferService = null;
dqp = null;
running = false;
this.shutdownListener.setShutdownInProgress(false);
}
private synchronized void checkStarted() {
if (running == null || !running) {
throw new IllegalStateException();
}
}
public TeiidDriver getDriver() {
checkStarted();
return driver;
}
@Override
public EventDistributor getEventDistributor() {
return this.eventDistributorFactoryService.getEventDistributor();
}
@SuppressWarnings("unchecked")
@Override
public ExecutionFactory<Object, Object> getExecutionFactory(String name)
throws ConnectorManagerException {
ExecutionFactory<?, ?> ef = translators.get(name);
if (ef == null) {
return TranslatorUtil.getExecutionFactory(name, this.translatorRepository, this.translatorRepository,
null, new IdentityHashMap<org.teiid.adminapi.Translator, ExecutionFactory<Object,Object>>(), new HashSet<String>());
}
return (ExecutionFactory<Object, Object>) ef;
}
@Override
protected VDBRepository getVDBRepository() {
return this.repo;
}
/**
* Get the effective ddl text for the given schema
* @param vdbName
* @param schemaName
* @return the ddl or null if the vdb/schema does not exist
*/
public String getSchemaDdl(String vdbName, String schemaName) {
VDBMetaData vdb = repo.getVDB(vdbName, "1"); //$NON-NLS-1$
if (vdb == null) {
return null;
}
TransformationMetadata metadata = vdb.getAttachment(TransformationMetadata.class);
if (metadata == null) {
return null;
}
Schema schema = metadata.getMetadataStore().getSchema(schemaName);
if (schema == null) {
return null;
}
return DDLStringVisitor.getDDLString(schema, null, null);
}
/**
* Return the bound port for the transport number
* @param transport
* @return
*/
public int getPort(int transport) {
return this.transports.get(transport).getPort();
}
TranslatorRepository getTranslatorRepository() {
return translatorRepository;
}
protected SessionAwareCache<CachedResults> getRsCache() {
return this.rs;
}
protected SessionAwareCache<PreparedPlan> getPpcCache() {
return this.ppc;
}
public Admin getAdmin() {
return EmbeddedAdminFactory.getInstance().createAdmin(this);
}
static class ShutDownListener implements ContainerLifeCycleListener {
private boolean shutdownInProgress = false;
private boolean bootInProgress = false;
private boolean running = false;
@Override
public boolean isShutdownInProgress() {
return shutdownInProgress;
}
@Override
public boolean isBootInProgress() {
return bootInProgress;
}
public void addListener(LifeCycleEventListener listener) {
}
public void setBootInProgress(boolean value) {
this.bootInProgress = value;
}
public void setShutdownInProgress(boolean value) {
this.shutdownInProgress = value;
}
public void started() {
running = true;
}
@Override
public boolean isStarted() {
return running;
}
}
public static void createPreParser(VDBMetaData deployment) throws TeiidException {
String preparserClass = deployment.getPropertyValue(VDBMetaData.PREPARSER_CLASS);
if (preparserClass != null) {
ClassLoader vdbClassLoader = deployment.getAttachment(ClassLoader.class);
PreParser preParser = (PreParser) ReflectionHelper.create(preparserClass, Collections.emptyList(), vdbClassLoader);
deployment.addAttchment(PreParser.class, preParser);
}
}
}