/* * JBoss, Home of Professional Open Source * Copyright 2010, Red Hat Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This 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 software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.ejb3.deployment.processors; import static java.security.AccessController.doPrivileged; import java.net.URI; import java.security.GeneralSecurityException; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import javax.net.ssl.SSLContext; import org.jboss.as.ee.component.Attachments; import org.jboss.as.ee.component.ComponentDescription; import org.jboss.as.ee.component.EEModuleDescription; import org.jboss.as.ejb3.deployment.EjbDeploymentAttachmentKeys; import org.jboss.as.ejb3.logging.EjbLogger; import org.jboss.as.ejb3.remote.EJBClientContextService; import org.jboss.as.ejb3.remote.RemotingProfileService; import org.jboss.as.remoting.AbstractOutboundConnectionService; 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.ejb.client.EJBClientContext; import org.jboss.modules.Module; import org.jboss.modules.ModuleClassLoader; import org.jboss.msc.service.Service; import org.jboss.msc.service.ServiceBuilder; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.StartContext; import org.jboss.msc.service.StartException; import org.jboss.msc.service.StopContext; import org.jboss.msc.value.InjectedValue; import org.jboss.remoting3.RemotingOptions; import org.wildfly.common.context.ContextManager; import org.wildfly.discovery.Discovery; import org.wildfly.security.auth.client.AuthenticationConfiguration; import org.wildfly.security.auth.client.AuthenticationContext; import org.wildfly.security.auth.client.AuthenticationContextConfigurationClient; import org.wildfly.security.auth.client.MatchRule; import org.xnio.OptionMap; /** * A deployment processor which associates the {@link EJBClientContext}, belonging to a deployment unit, * with the deployment unit's classloader. * * @author Stuart Douglas * @author Jaikiran Pai */ public class EjbClientContextSetupProcessor implements DeploymentUnitProcessor { private static final AuthenticationContextConfigurationClient CLIENT = doPrivileged(AuthenticationContextConfigurationClient.ACTION); @Override public void deploy(final DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException { final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); final Module module = deploymentUnit.getAttachment(org.jboss.as.server.deployment.Attachments.MODULE); if (module == null) { return; } RegistrationService registrationService = new RegistrationService(module); ServiceName registrationServiceName = deploymentUnit.getServiceName().append("ejb3","client-context","registration-service"); final ServiceName profileServiceName = getProfileServiceName(phaseContext); final ServiceBuilder<Void> builder = phaseContext.getServiceTarget().addService(registrationServiceName, registrationService) .addDependency(getEJBClientContextServiceName(phaseContext), EJBClientContextService.class, registrationService.ejbClientContextInjectedValue) .addDependency(getDiscoveryServiceName(phaseContext), Discovery.class, registrationService.discoveryInjector); if (profileServiceName != null) { builder.addDependency(profileServiceName, RemotingProfileService.class, registrationService.profileServiceInjectedValue); } builder.install(); final EEModuleDescription moduleDescription = deploymentUnit.getAttachment(Attachments.EE_MODULE_DESCRIPTION); if (moduleDescription == null) { return; } //we need to make sure all our components have a dependency on the EJB client context registration, which in turn implies a dependency on the context for(final ComponentDescription component : moduleDescription.getComponentDescriptions()) { component.addDependency(registrationServiceName, ServiceBuilder.DependencyType.REQUIRED); } } @Override public void undeploy(final DeploymentUnit deploymentUnit) { } private ServiceName getEJBClientContextServiceName(final DeploymentPhaseContext phaseContext) { final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); final DeploymentUnit parentDeploymentUnit = deploymentUnit.getParent(); // The top level parent deployment unit will have the attachment containing the EJB client context // service name ServiceName serviceName; if (parentDeploymentUnit != null) { serviceName = parentDeploymentUnit.getAttachment(EjbDeploymentAttachmentKeys.EJB_CLIENT_CONTEXT_SERVICE_NAME); } else { serviceName = deploymentUnit.getAttachment(EjbDeploymentAttachmentKeys.EJB_CLIENT_CONTEXT_SERVICE_NAME); } if (serviceName != null) { return serviceName; } return EJBClientContextService.DEFAULT_SERVICE_NAME; } private ServiceName getDiscoveryServiceName(final DeploymentPhaseContext phaseContext) { final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); final DeploymentUnit parentDeploymentUnit = deploymentUnit.getParent(); if (parentDeploymentUnit != null) { return DiscoveryService.BASE_NAME.append(parentDeploymentUnit.getName()); } else { return DiscoveryService.BASE_NAME.append(deploymentUnit.getName()); } } private ServiceName getProfileServiceName(final DeploymentPhaseContext phaseContext) { final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); final DeploymentUnit parentDeploymentUnit = deploymentUnit.getParent(); if (parentDeploymentUnit != null) { return parentDeploymentUnit.getAttachment(EjbDeploymentAttachmentKeys.EJB_REMOTING_PROFILE_SERVICE_NAME); } else { return deploymentUnit.getAttachment(EjbDeploymentAttachmentKeys.EJB_REMOTING_PROFILE_SERVICE_NAME); } } private static final class RegistrationService implements Service<Void> { private final Module module; final InjectedValue<EJBClientContextService> ejbClientContextInjectedValue = new InjectedValue<>(); final InjectedValue<Discovery> discoveryInjector = new InjectedValue<>(); final InjectedValue<RemotingProfileService> profileServiceInjectedValue = new InjectedValue<>(); private RegistrationService(Module module) { this.module = module; } @Override public void start(StartContext context) throws StartException { try { doPrivileged((PrivilegedExceptionAction<Void>) () -> { // associate the EJB client context and discovery setup with the deployment classloader final EJBClientContextService ejbClientContextService = ejbClientContextInjectedValue.getValue(); final EJBClientContext ejbClientContext = ejbClientContextService.getClientContext(); final AuthenticationContext ejbClientClustersAuthenticationContext = ejbClientContextService.getClustersAuthenticationContext(); final ModuleClassLoader classLoader = module.getClassLoader(); EjbLogger.DEPLOYMENT_LOGGER.debugf("Registering EJB client context %s for classloader %s", ejbClientContext, classLoader); final ContextManager<AuthenticationContext> authenticationContextManager = AuthenticationContext.getContextManager(); final RemotingProfileService profileService = profileServiceInjectedValue.getOptionalValue(); if (profileService != null || ejbClientClustersAuthenticationContext != null) { // this is cheating but it works for our purposes AuthenticationContext authenticationContext = authenticationContextManager.getClassLoaderDefault(classLoader); if (authenticationContext == null) { authenticationContext = authenticationContextManager.get(); } // now transform it if (profileService != null) { for (RemotingProfileService.ConnectionSpec connectionSpec : profileService.getConnectionSpecs()) { authenticationContext = transformOne(connectionSpec, authenticationContext); } } if (ejbClientClustersAuthenticationContext != null) { authenticationContext = ejbClientClustersAuthenticationContext.with(authenticationContext); } // and set the result authenticationContextManager.setClassLoaderDefault(classLoader, authenticationContext); } EJBClientContext.getContextManager().setClassLoaderDefault(classLoader, ejbClientContext); Discovery.getContextManager().setClassLoaderDefault(classLoader, discoveryInjector.getValue()); return null; }); } catch (PrivilegedActionException e) { throw (StartException) e.getCause(); } } @Override public void stop(StopContext context) { // de-associate the EJB client context with the deployment classloader doPrivileged((PrivilegedAction<Void>) () -> { final ModuleClassLoader classLoader = module.getClassLoader(); EjbLogger.DEPLOYMENT_LOGGER.debugf("unRegistering EJB client context for classloader %s", classLoader); EJBClientContext.getContextManager().setClassLoaderDefault(classLoader, null); Discovery.getContextManager().setClassLoaderDefault(classLoader, null); // this is redundant but should be safe AuthenticationContext.getContextManager().setClassLoaderDefault(classLoader, null); return null; }); } @Override public Void getValue() throws IllegalStateException, IllegalArgumentException { return null; } private static AuthenticationContext transformOne(RemotingProfileService.ConnectionSpec connectionSpec, AuthenticationContext context) throws StartException { final AbstractOutboundConnectionService connectionService = connectionSpec.getInjector().getValue(); AuthenticationConfiguration authenticationConfiguration = connectionService.getAuthenticationConfiguration(); SSLContext sslContext = connectionService.getSSLContext(); final URI destinationUri = connectionService.getDestinationUri(); MatchRule rule = MatchRule.ALL; final String scheme = destinationUri.getScheme(); if (scheme != null) { rule = rule.matchProtocol(scheme); } final String host = destinationUri.getHost(); if (host != null) { rule = rule.matchHost(host); } final int port = destinationUri.getPort(); if (port != -1) { rule = rule.matchPort(port); } final String path = destinationUri.getPath(); if (path != null && ! path.isEmpty()) { rule = rule.matchPath(path); } final OptionMap connectOptions = connectionSpec.getConnectOptions(); authenticationConfiguration = RemotingOptions.mergeOptionsIntoAuthenticationConfiguration(connectOptions, authenticationConfiguration); AuthenticationConfiguration configuration = CLIENT.getAuthenticationConfiguration(destinationUri, context, - 1, "ejb", "jboss", null); if (sslContext == null) { try { sslContext = CLIENT.getSSLContext(destinationUri, context); } catch (GeneralSecurityException e) { throw EjbLogger.ROOT_LOGGER.failedToObtainSSLContext(e); } } final SSLContext finalSSLContext = sslContext; AuthenticationContext mergedAuthenticationContext = context.with(0, rule, configuration.with(authenticationConfiguration)); return mergedAuthenticationContext.withSsl(0, rule, () -> finalSSLContext); } } }