/* * 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.jboss; import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import javax.script.ScriptEngineManager; import org.jboss.as.naming.deployment.ContextNames; import org.jboss.as.server.deployment.Attachments; import org.jboss.as.server.deployment.DeploymentPhaseContext; import org.jboss.as.server.deployment.DeploymentUnit; import org.jboss.as.server.deployment.DeploymentUnitProcessingException; import org.jboss.as.server.deployment.DeploymentUnitProcessor; import org.jboss.modules.ModuleClassLoader; import org.jboss.msc.service.*; import org.jboss.msc.service.ServiceController.Mode; import org.jboss.msc.service.ServiceController.State; import org.jboss.msc.value.InjectedValue; import org.jboss.vfs.VirtualFile; import org.teiid.adminapi.Model; import org.teiid.adminapi.Translator; import org.teiid.adminapi.VDBImport; import org.teiid.adminapi.impl.ModelMetaData; import org.teiid.adminapi.impl.SourceMappingMetadata; import org.teiid.adminapi.impl.VDBMetaData; import org.teiid.adminapi.impl.VDBTranslatorMetaData; import org.teiid.core.TeiidException; import org.teiid.deployers.RuntimeVDB; import org.teiid.deployers.TranslatorUtil; import org.teiid.deployers.UDFMetaData; import org.teiid.deployers.VDBRepository; import org.teiid.deployers.VDBStatusChecker; import org.teiid.dqp.internal.datamgr.TranslatorRepository; import org.teiid.jboss.TeiidServiceNames.InvalidServiceNameException; import org.teiid.logging.LogConstants; import org.teiid.logging.LogManager; import org.teiid.metadata.index.IndexMetadataRepository; import org.teiid.query.metadata.VDBResources; import org.teiid.runtime.EmbeddedServer; import org.teiid.runtime.RuntimePlugin; import org.teiid.vdb.runtime.VDBKey; class VDBDeployer implements DeploymentUnitProcessor { private static final String JAVA_CONTEXT = "java:/"; //$NON-NLS-1$ private TranslatorRepository translatorRepository; private VDBRepository vdbRepository; JBossLifeCycleListener shutdownListener; public VDBDeployer(TranslatorRepository translatorRepo, VDBRepository vdbRepo, JBossLifeCycleListener shutdownListener) { this.translatorRepository = translatorRepo; this.vdbRepository = vdbRepo; this.shutdownListener = shutdownListener; } public void deploy(final DeploymentPhaseContext context) throws DeploymentUnitProcessingException { final DeploymentUnit deploymentUnit = context.getDeploymentUnit(); if (!TeiidAttachments.isVDBDeployment(deploymentUnit)) { return; } final VDBMetaData deployment = deploymentUnit.getAttachment(TeiidAttachments.VDB_METADATA); VDBMetaData other = this.vdbRepository.getVDB(deployment.getName(), deployment.getVersion()); if (other != null) { String deploymentName = other.getPropertyValue(TranslatorUtil.DEPLOYMENT_NAME); throw new DeploymentUnitProcessingException(IntegrationPlugin.Util.gs(IntegrationPlugin.Event.TEIID50106, deployment, deploymentName)); } deployment.addProperty(TranslatorUtil.DEPLOYMENT_NAME, deploymentUnit.getName()); // check to see if there is old vdb already deployed. final ServiceController<?> controller = context.getServiceRegistry().getService(TeiidServiceNames.vdbServiceName(deployment.getName(), deployment.getVersion())); if (controller != null) { LogManager.logInfo(LogConstants.CTX_RUNTIME, IntegrationPlugin.Util.gs(IntegrationPlugin.Event.TEIID50019, deployment)); controller.setMode(ServiceController.Mode.REMOVE); } boolean preview = deployment.isPreview(); if (!preview && deployment.hasErrors()) { throw new DeploymentUnitProcessingException(IntegrationPlugin.Util.gs(IntegrationPlugin.Event.TEIID50074, deployment)); } // make sure the translator defined exists in configuration; otherwise add as error for (ModelMetaData model:deployment.getModelMetaDatas().values()) { if (!model.isSource() || model.getSourceNames().isEmpty()) { continue; } for (String source:model.getSourceNames()) { String translatorName = model.getSourceTranslatorName(source); if (deployment.isOverideTranslator(translatorName)) { VDBTranslatorMetaData parent = deployment.getTranslator(translatorName); translatorName = parent.getType(); } Translator translator = this.translatorRepository.getTranslatorMetaData(translatorName); if ( translator == null) { String msg = IntegrationPlugin.Util.gs(IntegrationPlugin.Event.TEIID50077, translatorName, deployment.getName(), deployment.getVersion()); LogManager.logWarning(LogConstants.CTX_RUNTIME, msg); } } } // add VDB module's classloader as an attachment ModuleClassLoader classLoader = deploymentUnit.getAttachment(Attachments.MODULE).getClassLoader(); deployment.addAttchment(ClassLoader.class, classLoader); deployment.addAttchment(ScriptEngineManager.class, new ScriptEngineManager(classLoader)); try { EmbeddedServer.createPreParser(deployment); } catch (TeiidException e1) { throw new DeploymentUnitProcessingException(e1); } UDFMetaData udf = deploymentUnit.removeAttachment(TeiidAttachments.UDF_METADATA); if (udf == null) { udf = new UDFMetaData(); } udf.setFunctionClassLoader(classLoader); deployment.addAttchment(UDFMetaData.class, udf); VirtualFile file = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_ROOT).getRoot(); VDBResources resources; try { resources = new VDBResources(file, deployment); } catch (IOException e) { throw new DeploymentUnitProcessingException(IntegrationPlugin.Event.TEIID50017.name(), e); } this.vdbRepository.addPendingDeployment(deployment); // build a VDB service final VDBService vdb = new VDBService(deployment, resources, shutdownListener); vdb.addMetadataRepository("index", new IndexMetadataRepository()); //$NON-NLS-1$ final ServiceBuilder<RuntimeVDB> vdbService = context.getServiceTarget().addService(TeiidServiceNames.vdbServiceName(deployment.getName(), deployment.getVersion()), vdb); // add dependencies to data-sources dataSourceDependencies(deployment, context.getServiceTarget()); for (VDBImport vdbImport : deployment.getVDBImports()) { VDBKey vdbKey = new VDBKey(vdbImport.getName(), vdbImport.getVersion()); if (vdbKey.isAtMost()) { //TODO: could allow partial versions here if we canonicalize throw new DeploymentUnitProcessingException(RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40144, deployment, vdbKey)); } vdbService.addDependency(TeiidServiceNames.vdbFinishedServiceName(vdbImport.getName(), vdbKey.getVersion())); } // adding the translator services is redundant, however if one is removed then it is an issue. for (Model model:deployment.getModels()) { List<String> sourceNames = model.getSourceNames(); for (String sourceName:sourceNames) { String translatorName = model.getSourceTranslatorName(sourceName); if (deployment.isOverideTranslator(translatorName)) { VDBTranslatorMetaData translator = deployment.getTranslator(translatorName); translatorName = translator.getType(); } vdbService.addDependency(TeiidServiceNames.translatorServiceName(translatorName)); } } ServiceName vdbSwitchServiceName = TeiidServiceNames.vdbSwitchServiceName(deployment.getName(), deployment.getVersion()); vdbService.addDependency(TeiidServiceNames.VDB_REPO, VDBRepository.class, vdb.vdbRepositoryInjector); vdbService.addDependency(TeiidServiceNames.TRANSLATOR_REPO, TranslatorRepository.class, vdb.translatorRepositoryInjector); vdbService.addDependency(TeiidServiceNames.THREAD_POOL_SERVICE, Executor.class, vdb.executorInjector); vdbService.addDependency(TeiidServiceNames.OBJECT_SERIALIZER, ObjectSerializer.class, vdb.serializerInjector); vdbService.addDependency(TeiidServiceNames.VDB_STATUS_CHECKER, VDBStatusChecker.class, vdb.vdbStatusCheckInjector); vdbService.addDependency(vdbSwitchServiceName, CountDownLatch.class, new InjectedValue<CountDownLatch>()); // VDB restart switch, control the vdbservice by adding removing the switch service. If you // remove the service by setting status remove, there is no way start it back up if vdbservice used alone installVDBSwitchService(context.getServiceTarget(), vdbSwitchServiceName); vdbService.addListener(new AbstractServiceListener<Object>() { @Override public void transition(final ServiceController controller, final ServiceController.Transition transition) { if (transition.equals(ServiceController.Transition.DOWN_to_WAITING)) { RuntimeVDB runtimeVDB = RuntimeVDB.class.cast(controller.getValue()); if (runtimeVDB != null && runtimeVDB.isRestartInProgress()) { ServiceName vdbSwitchServiceName = TeiidServiceNames.vdbSwitchServiceName(deployment.getName(), deployment.getVersion()); ServiceController<?> switchSvc = controller.getServiceContainer().getService(vdbSwitchServiceName); if (switchSvc != null) { CountDownLatch latch = CountDownLatch.class.cast(switchSvc.getValue()); try { latch.await(5, TimeUnit.SECONDS); } catch (InterruptedException e) { // todo:log it? } } installVDBSwitchService(controller.getServiceContainer(), vdbSwitchServiceName); } } } }); vdbService.setInitialMode(Mode.PASSIVE).install(); } private void installVDBSwitchService(final ServiceTarget serviceTarget, ServiceName vdbSwitchServiceName) { // install switch service now. ServiceBuilder<CountDownLatch> svc = serviceTarget.addService(vdbSwitchServiceName, new Service<CountDownLatch>() { private CountDownLatch latch = new CountDownLatch(1); @Override public CountDownLatch getValue() throws IllegalStateException,IllegalArgumentException { return this.latch; } @Override public void start(StartContext context) throws StartException { } @Override public void stop(StopContext context) { } }); svc.addListener(new AbstractServiceListener<Object>() { @Override public void transition(final ServiceController controller, final ServiceController.Transition transition) { if (transition.equals(ServiceController.Transition.REMOVING_to_REMOVED)) { CountDownLatch latch = CountDownLatch.class.cast(controller.getValue()); latch.countDown(); } } }); svc.install(); } static void addDataSourceListener( final ServiceTarget serviceTarget, final VDBKey vdbKey, final String dsName) { final String jndiName = getJndiName(dsName); ServiceName dsListenerServiceName; try { dsListenerServiceName = TeiidServiceNames.dsListenerServiceName(vdbKey.getName(), vdbKey.getVersion(), dsName); } catch (InvalidServiceNameException e) { LogManager.logWarning(LogConstants.CTX_RUNTIME, e, e.getMessage()); return; } ContextNames.BindInfo bindInfo = ContextNames.bindInfoFor(jndiName); final ServiceName svcName = bindInfo.getBinderServiceName(); DataSourceListener dsl = new DataSourceListener(dsName, svcName, vdbKey); ServiceBuilder<DataSourceListener> sb = serviceTarget.addService(dsListenerServiceName, dsl); sb.addDependency(svcName); sb.addDependency(TeiidServiceNames.VDB_STATUS_CHECKER, VDBStatusChecker.class, dsl.vdbStatusCheckInjector); sb.setInitialMode(Mode.PASSIVE).install(); } private void dataSourceDependencies(VDBMetaData deployment, ServiceTarget serviceTarget) { final VDBKey vdbKey = new VDBKey(deployment.getName(), deployment.getVersion()); Set<String> dataSources = new HashSet<String>(); for (ModelMetaData model:deployment.getModelMetaDatas().values()) { for (String sourceName:model.getSourceNames()) { // Need to make the data source service as dependency; otherwise dynamic vdbs will not work correctly. String dsName = model.getSourceConnectionJndiName(sourceName); if (dsName == null) { continue; } if (!dataSources.add(VDBStatusChecker.stripContext(dsName))) { continue; //already listening } addDataSourceListener(serviceTarget, vdbKey, dsName); } } } static class DataSourceListener implements Service<DataSourceListener>{ private String dsName; private ServiceName svcName; private VDBKey vdb; InjectedValue<VDBStatusChecker> vdbStatusCheckInjector = new InjectedValue<VDBStatusChecker>(); public DataSourceListener(String dsName, ServiceName svcName, VDBKey vdb) { this.dsName = dsName; this.svcName = svcName; this.vdb = vdb; } public DataSourceListener getValue() throws IllegalStateException,IllegalArgumentException { return this; } @Override public void start(StartContext context) throws StartException { ServiceController<?> s = context.getController().getServiceContainer().getService(this.svcName); if (s != null) { this.vdbStatusCheckInjector.getValue().dataSourceAdded(this.dsName, vdb); } } @Override public void stop(StopContext context) { ServiceController<?> s = context.getController().getServiceContainer().getService(this.svcName); if (s.getMode().equals(Mode.REMOVE) || s.getState().equals(State.STOPPING)) { this.vdbStatusCheckInjector.getValue().dataSourceRemoved(this.dsName, vdb); } } } public static String getJndiName(String name) { String jndiName = name; if (!name.startsWith(JAVA_CONTEXT)) { jndiName = JAVA_CONTEXT + jndiName; } return jndiName; } @Override public void undeploy(final DeploymentUnit deploymentUnit) { if (!TeiidAttachments.isVDBDeployment(deploymentUnit)) { return; } final VDBMetaData deployment = deploymentUnit.getAttachment(TeiidAttachments.VDB_METADATA); if (!this.shutdownListener.isShutdownInProgress()) { final VDBMetaData vdb = deploymentUnit.getAttachment(TeiidAttachments.VDB_METADATA); ServiceController<?> sc = deploymentUnit.getServiceRegistry().getService(TeiidServiceNames.OBJECT_SERIALIZER); if (sc != null) { ObjectSerializer serilalizer = ObjectSerializer.class.cast(sc.getValue()); serilalizer.removeAttachments(vdb); LogManager.logTrace(LogConstants.CTX_RUNTIME, "VDB "+vdb.getName()+" metadata removed"); //$NON-NLS-1$ //$NON-NLS-2$ } } this.vdbRepository.removeVDB(deployment.getName(), deployment.getVersion()); ServiceController<?> switchSvc = deploymentUnit.getServiceRegistry().getService(TeiidServiceNames.vdbSwitchServiceName(deployment.getName(), deployment.getVersion())); if (switchSvc != null) { switchSvc.setMode(ServiceController.Mode.REMOVE); } for (ModelMetaData model:deployment.getModelMetaDatas().values()) { for (SourceMappingMetadata smm:model.getSources().values()) { String dsName = smm.getConnectionJndiName(); if (dsName == null) { continue; } ServiceController<?> dsService; try { dsService = deploymentUnit.getServiceRegistry().getService(TeiidServiceNames.dsListenerServiceName(deployment.getName(), deployment.getVersion(), dsName)); } catch (InvalidServiceNameException e) { continue; } if (dsService != null) { dsService.setMode(ServiceController.Mode.REMOVE); } } } final ServiceController<?> controller = deploymentUnit.getServiceRegistry().getService(TeiidServiceNames.vdbServiceName(deployment.getName(), deployment.getVersion())); if (controller != null) { controller.setMode(ServiceController.Mode.REMOVE); } } }