/* * JBoss, Home of Professional Open Source. * Copyright 2012, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file 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.mail.extension; import static org.jboss.as.mail.extension.MailServerDefinition.OUTBOUND_SOCKET_BINDING_CAPABILITY_NAME; import static org.jboss.as.mail.extension.MailSessionDefinition.ATTRIBUTES; import static org.jboss.as.mail.extension.MailSessionDefinition.SESSION_CAPABILITY; import static org.jboss.as.mail.extension.MailSubsystemModel.CUSTOM; import static org.jboss.as.mail.extension.MailSubsystemModel.IMAP; import static org.jboss.as.mail.extension.MailSubsystemModel.POP3; import static org.jboss.as.mail.extension.MailSubsystemModel.SERVER_TYPE; import static org.jboss.as.mail.extension.MailSubsystemModel.SMTP; import static org.jboss.as.mail.extension.MailSubsystemModel.USER_NAME; import java.util.Map; import javax.mail.Session; import org.jboss.as.controller.AbstractAddStepHandler; import org.jboss.as.controller.CapabilityServiceBuilder; import org.jboss.as.controller.CapabilityServiceTarget; import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.registry.Resource; import org.jboss.as.controller.security.CredentialReference; import org.jboss.as.naming.ManagedReferenceFactory; import org.jboss.as.naming.ServiceBasedNamingStore; import org.jboss.as.naming.deployment.ContextNames; import org.jboss.as.naming.service.BinderService; import org.jboss.as.network.OutboundSocketBinding; import org.jboss.dmr.ModelNode; import org.jboss.dmr.Property; import org.jboss.msc.service.AbstractServiceListener; import org.jboss.msc.service.ServiceBuilder; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceName; /** * @author Tomaz Cerar * @created 27.7.11 0:55 */ class MailSessionAdd extends AbstractAddStepHandler { static final MailSessionAdd INSTANCE = new MailSessionAdd(); @Deprecated static final ServiceName MAIL_SESSION_SERVICE_NAME = ServiceName.JBOSS.append("mail-session"); protected MailSessionAdd() { super(ATTRIBUTES); } /** * Make any runtime changes necessary to effect the changes indicated by the given {@code operation}. E * <p> * It constructs a MailSessionService that provides mail session and registers it to Naming service. * </p> * * @param context the operation context * @param operation the operation being executed * @param model persistent configuration model node that corresponds to the address of {@code operation} * @throws org.jboss.as.controller.OperationFailedException * if {@code operation} is invalid or updating the runtime otherwise fails */ @Override protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { final PathAddress address = context.getCurrentAddress(); ModelNode fullTree = Resource.Tools.readModel(context.readResource(PathAddress.EMPTY_ADDRESS)); installRuntimeServices(context, address, fullTree); } private static void addCredentialStoreReference(ServerConfig serverConfig, OperationContext context, ModelNode model, ServiceBuilder<?> serviceBuilder, String... modelFilter) throws OperationFailedException { if (serverConfig != null) { ModelNode filteredModelNode = model; if (modelFilter != null && modelFilter.length > 0) { for (String path : modelFilter) { if (filteredModelNode.get(path).isDefined()) filteredModelNode = filteredModelNode.get(path); else break; } } ModelNode value = MailServerDefinition.CREDENTIAL_REFERENCE.resolveModelAttribute(context, filteredModelNode); if (value.isDefined()) { serverConfig.getCredentialSourceSupplierInjector() .inject( CredentialReference.getCredentialSourceSupplier(context, MailServerDefinition.CREDENTIAL_REFERENCE, filteredModelNode, serviceBuilder)); } } } static void installRuntimeServices(OperationContext context, PathAddress address, ModelNode fullModel) throws OperationFailedException { final String jndiName = getJndiName(fullModel, context); final CapabilityServiceTarget serviceTarget = context.getCapabilityServiceTarget(); final MailSessionConfig config = from(context, fullModel); final MailSessionService service = new MailSessionService(config); final CapabilityServiceBuilder<Session> mailSessionBuilder = serviceTarget.addCapability(SESSION_CAPABILITY.fromBaseCapability(address.getLastElement().getValue()), service); addOutboundSocketDependency(service, mailSessionBuilder, config.getImapServer()); addCredentialStoreReference(config.getImapServer(), context, fullModel, mailSessionBuilder, MailSubsystemModel.IMAP_SERVER_PATH.getKey(), MailSubsystemModel.IMAP_SERVER_PATH.getValue()); addOutboundSocketDependency(service, mailSessionBuilder, config.getPop3Server()); addCredentialStoreReference(config.getPop3Server(), context, fullModel, mailSessionBuilder, MailSubsystemModel.POP3_SERVER_PATH.getKey(), MailSubsystemModel.POP3_SERVER_PATH.getValue()); addOutboundSocketDependency(service, mailSessionBuilder, config.getSmtpServer()); addCredentialStoreReference(config.getSmtpServer(), context, fullModel, mailSessionBuilder, MailSubsystemModel.SMTP_SERVER_PATH.getKey(), MailSubsystemModel.SMTP_SERVER_PATH.getValue()); for (CustomServerConfig server : config.getCustomServers()) { if (server.getOutgoingSocketBinding() != null) { addOutboundSocketDependency(service, mailSessionBuilder, server); } addCredentialStoreReference(server, context, fullModel, mailSessionBuilder, MailSubsystemModel.CUSTOM_SERVER_PATH.getKey(), server.getProtocol()); } mailSessionBuilder.addAliases(MAIL_SESSION_SERVICE_NAME.append(address.getLastElement().getValue())); final ManagedReferenceFactory valueManagedReferenceFactory = new MailSessionManagedReferenceFactory(service); final ContextNames.BindInfo bindInfo = ContextNames.bindInfoFor(jndiName); final BinderService binderService = new BinderService(bindInfo.getBindName()); final ServiceBuilder<?> binderBuilder = serviceTarget .addService(bindInfo.getBinderServiceName(), binderService) .addInjection(binderService.getManagedObjectInjector(), valueManagedReferenceFactory) .addDependency(bindInfo.getParentContextServiceName(), ServiceBasedNamingStore.class, binderService.getNamingStoreInjector()).addListener(new AbstractServiceListener<Object>() { public void transition(final ServiceController<? extends Object> controller, final ServiceController.Transition transition) { switch (transition) { case STARTING_to_UP: { MailLogger.ROOT_LOGGER.boundMailSession(jndiName); break; } case START_REQUESTED_to_DOWN: { MailLogger.ROOT_LOGGER.unboundMailSession(jndiName); break; } case REMOVING_to_REMOVED: { MailLogger.ROOT_LOGGER.removedMailSession(jndiName); break; } } } }); mailSessionBuilder .setInitialMode(ServiceController.Mode.ACTIVE) .install(); binderBuilder .setInitialMode(ServiceController.Mode.ACTIVE) .install(); } /** * Extracts the raw JNDI_NAME value from the given model node, and depending on the value and * the value of any USE_JAVA_CONTEXT child node, converts the raw name into a compliant jndi name. * * @param modelNode the model node; either an operation or the model behind a mail session resource * @return the compliant jndi name */ static String getJndiName(final ModelNode modelNode, OperationContext context) throws OperationFailedException { final String rawJndiName = MailSessionDefinition.JNDI_NAME.resolveModelAttribute(context, modelNode).asString(); return getJndiName(rawJndiName); } public static String getJndiName(final String rawJndiName) { final String jndiName; if (!rawJndiName.startsWith("java:")) { jndiName = "java:jboss/mail/" + rawJndiName; } else { jndiName = rawJndiName; } return jndiName; } private static void addOutboundSocketDependency(MailSessionService service, CapabilityServiceBuilder<?> mailSessionBuilder, ServerConfig server) { if (server != null) { final String ref = server.getOutgoingSocketBinding(); mailSessionBuilder.addCapabilityRequirement(OUTBOUND_SOCKET_BINDING_CAPABILITY_NAME, OutboundSocketBinding.class, service.getSocketBindingInjector(ref), ref); } } static MailSessionConfig from(final OperationContext operationContext, final ModelNode model) throws OperationFailedException { MailSessionConfig cfg = new MailSessionConfig(); cfg.setJndiName(MailSessionDefinition.JNDI_NAME.resolveModelAttribute(operationContext, model).asString()); cfg.setDebug(MailSessionDefinition.DEBUG.resolveModelAttribute(operationContext, model).asBoolean()); if (MailSessionDefinition.FROM.resolveModelAttribute(operationContext, model).isDefined()) { cfg.setFrom(MailSessionDefinition.FROM.resolveModelAttribute(operationContext, model).asString()); } if (model.hasDefined(SERVER_TYPE)) { ModelNode server = model.get(SERVER_TYPE); if (server.hasDefined(SMTP)) { cfg.setSmtpServer(readServerConfig(operationContext, server.get(SMTP))); } if (server.hasDefined(POP3)) { cfg.setPop3Server(readServerConfig(operationContext, server.get(POP3))); } if (server.hasDefined(IMAP)) { cfg.setImapServer(readServerConfig(operationContext, server.get(IMAP))); } } if (model.hasDefined(CUSTOM)) { for (Property server : model.get(CUSTOM).asPropertyList()) { cfg.addCustomServer(readCustomServerConfig(server.getName(), operationContext, server.getValue())); } } return cfg; } private static ServerConfig readServerConfig(final OperationContext operationContext, final ModelNode model) throws OperationFailedException { final String socket = MailServerDefinition.OUTBOUND_SOCKET_BINDING_REF.resolveModelAttribute(operationContext, model).asString(); final Credentials credentials = readCredentials(operationContext, model); boolean ssl = MailServerDefinition.SSL.resolveModelAttribute(operationContext, model).asBoolean(); boolean tls = MailServerDefinition.TLS.resolveModelAttribute(operationContext, model).asBoolean(); return new ServerConfig(socket, credentials, ssl, tls, null); } private static CustomServerConfig readCustomServerConfig(final String protocol, final OperationContext operationContext, final ModelNode model) throws OperationFailedException { final ModelNode socketModel = MailServerDefinition.OUTBOUND_SOCKET_BINDING_REF_OPTIONAL.resolveModelAttribute(operationContext, model); final String socket = socketModel.isDefined() ? socketModel.asString() : null; final Credentials credentials = readCredentials(operationContext, model); boolean ssl = MailServerDefinition.SSL.resolveModelAttribute(operationContext, model).asBoolean(); boolean tls = MailServerDefinition.TLS.resolveModelAttribute(operationContext, model).asBoolean(); Map<String, String> properties = MailServerDefinition.PROPERTIES.unwrap(operationContext, model); return new CustomServerConfig(protocol, socket, credentials, ssl, tls, properties); } private static Credentials readCredentials(final OperationContext operationContext, final ModelNode model) throws OperationFailedException { if (model.get(USER_NAME).isDefined()) { String un = MailServerDefinition.USERNAME.resolveModelAttribute(operationContext, model).asString(); String pw = MailServerDefinition.PASSWORD.resolveModelAttribute(operationContext, model).asStringOrNull(); ModelNode value = MailServerDefinition.CREDENTIAL_REFERENCE.resolveValue(operationContext, model); String secret = null; if (value.isDefined()) { secret = CredentialReference.credentialReferencePartAsStringIfDefined(value, CredentialReference.CLEAR_TEXT); } if (secret != null) { return new Credentials(un, secret); } else { return new Credentials(un, pw); } } return null; } }