Package org.springframework.roo.addon.jms

Source Code of org.springframework.roo.addon.jms.JmsOperationsImpl

package org.springframework.roo.addon.jms;

import static java.lang.reflect.Modifier.PRIVATE;
import static java.lang.reflect.Modifier.PUBLIC;
import static java.lang.reflect.Modifier.TRANSIENT;
import static org.springframework.roo.model.JavaType.OBJECT;
import static org.springframework.roo.model.SpringJavaType.ASYNC;
import static org.springframework.roo.model.SpringJavaType.AUTOWIRED;
import static org.springframework.roo.model.SpringJavaType.JMS_OPERATIONS;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.springframework.roo.addon.propfiles.PropFileOperations;
import org.springframework.roo.classpath.PhysicalTypeCategory;
import org.springframework.roo.classpath.PhysicalTypeIdentifier;
import org.springframework.roo.classpath.TypeLocationService;
import org.springframework.roo.classpath.TypeManagementService;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetailsBuilder;
import org.springframework.roo.classpath.details.FieldMetadataBuilder;
import org.springframework.roo.classpath.details.MethodMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.classpath.itd.InvocableMemberBodyBuilder;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.project.Dependency;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.project.Path;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.support.util.DomUtils;
import org.springframework.roo.support.util.FileUtils;
import org.springframework.roo.support.util.XmlElementBuilder;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
* Provides JMS configuration operations.
*
* @author Stefan Schmidt
* @author Alan Stewart
* @since 1.0
*/
@Component
@Service
public class JmsOperationsImpl implements JmsOperations {

    @Reference private FileManager fileManager;
    @Reference private ProjectOperations projectOperations;
    @Reference private PropFileOperations propFileOperations;
    @Reference private TypeLocationService typeLocationService;
    @Reference private TypeManagementService typeManagementService;

    private void addDefaultDestination(final Document appCtx, final String name) {
        // If we do already have a default destination configured then do
        // nothing
        final Element root = appCtx.getDocumentElement();
        if (null != XmlUtils
                .findFirstElement(
                        "/beans/bean[@class = 'org.springframework.jms.core.JmsTemplate']/property[@name = 'defaultDestination']",
                        root)) {
            return;
        }
        // Otherwise add it
        final Element jmsTemplate = XmlUtils
                .findRequiredElement(
                        "/beans/bean[@class = 'org.springframework.jms.core.JmsTemplate']",
                        root);
        final Element defaultDestination = appCtx.createElement("property");
        defaultDestination.setAttribute("ref", name);
        defaultDestination.setAttribute("name", "defaultDestination");
        jmsTemplate.appendChild(defaultDestination);
    }

    public void addJmsListener(final JavaType targetType, final String name,
            final JmsDestinationType destinationType) {
        Validate.notNull(targetType, "Java type required");

        final String declaredByMetadataId = PhysicalTypeIdentifier
                .createIdentifier(targetType, projectOperations
                        .getPathResolver().getFocusedPath(Path.SRC_MAIN_JAVA));

        final List<MethodMetadataBuilder> methods = new ArrayList<MethodMetadataBuilder>();
        final List<JavaType> parameterTypes = Arrays.asList(OBJECT);
        final List<JavaSymbolName> parameterNames = Arrays
                .asList(new JavaSymbolName("message"));

        // Create some method content to get people started
        final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
        bodyBuilder
                .appendFormalLine("System.out.println(\"JMS message received: \" + message);");
        methods.add(new MethodMetadataBuilder(declaredByMetadataId, PUBLIC,
                new JavaSymbolName("onMessage"), JavaType.VOID_PRIMITIVE,
                AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
                parameterNames, bodyBuilder));

        final ClassOrInterfaceTypeDetailsBuilder cidBuilder = new ClassOrInterfaceTypeDetailsBuilder(
                declaredByMetadataId, PUBLIC, targetType,
                PhysicalTypeCategory.CLASS);
        cidBuilder.setDeclaredMethods(methods);

        // Determine the canonical filename
        final String physicalLocationCanonicalPath = getPhysicalLocationCanonicalPath(declaredByMetadataId);

        // Check the file doesn't already exist
        Validate.isTrue(!fileManager.exists(physicalLocationCanonicalPath),
                "%s already exists", projectOperations.getPathResolver()
                        .getFriendlyName(physicalLocationCanonicalPath));

        typeManagementService.createOrUpdateTypeOnDisk(cidBuilder.build());

        final String jmsContextPath = projectOperations.getPathResolver()
                .getFocusedIdentifier(Path.SPRING_CONFIG_ROOT,
                        "applicationContext-jms.xml");
        final Document document = XmlUtils.readXml(fileManager
                .getInputStream(jmsContextPath));
        final Element root = document.getDocumentElement();

        Element listenerContainer = DomUtils.findFirstElementByName(
                "jms:listener-container", root);
        if (listenerContainer != null
                && destinationType.name().equalsIgnoreCase(
                        listenerContainer.getAttribute("destination-type"))) {
            listenerContainer = document
                    .createElement("jms:listener-container");
            listenerContainer.setAttribute("connection-factory", "jmsFactory");
            listenerContainer.setAttribute("destination-type", destinationType
                    .name().toLowerCase());
            root.appendChild(listenerContainer);
        }

        if (listenerContainer != null) {
            final Element jmsListener = document.createElement("jms:listener");
            jmsListener.setAttribute("ref",
                    StringUtils.uncapitalize(targetType.getSimpleTypeName()));
            jmsListener.setAttribute("method", "onMessage");
            jmsListener.setAttribute("destination", name);

            final Element bean = document.createElement("bean");
            bean.setAttribute("class", targetType.getFullyQualifiedTypeName());
            bean.setAttribute("id",
                    StringUtils.uncapitalize(targetType.getSimpleTypeName()));
            root.appendChild(bean);

            listenerContainer.appendChild(jmsListener);
        }

        fileManager.createOrUpdateTextFileIfRequired(jmsContextPath,
                XmlUtils.nodeToString(document), false);
    }

    /**
     * Creates the injected method that sends a JMS message
     *
     * @param fieldName
     * @param declaredByMetadataId
     * @param asynchronous whether the JMS message should be sent asynchronously
     * @return a non-<code>null</code> builder
     */
    private MethodMetadataBuilder createSendMessageMethod(
            final JavaSymbolName fieldName, final String declaredByMetadataId,
            final boolean asynchronous) {
        final List<JavaType> parameterTypes = Arrays.asList(JavaType.OBJECT);
        final List<JavaSymbolName> parameterNames = Arrays
                .asList(new JavaSymbolName("messageObject"));

        final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
        bodyBuilder.appendFormalLine(fieldName
                + ".convertAndSend(messageObject);");

        final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
                declaredByMetadataId, PUBLIC,
                new JavaSymbolName("sendMessage"), JavaType.VOID_PRIMITIVE,
                AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
                parameterNames, bodyBuilder);
        if (asynchronous) {
            methodBuilder.addAnnotation(new AnnotationMetadataBuilder(ASYNC));
        }
        return methodBuilder;
    }

    /**
     * Ensures that the Spring config files contain the necessary elements and
     * properties to support asynchronous tasks.
     */
    private void ensureSpringAsynchronousSupportEnabled() {
        final String contextPath = projectOperations.getPathResolver()
                .getFocusedIdentifier(Path.SPRING_CONFIG_ROOT,
                        "applicationContext.xml");
        final Document appContext = XmlUtils.readXml(fileManager
                .getInputStream(contextPath));
        final Element root = appContext.getDocumentElement();

        if (DomUtils.findFirstElementByName("task:annotation-driven", root) == null) {
            if (root.getAttribute("xmlns:task").length() == 0) {
                root.setAttribute("xmlns:task",
                        "http://www.springframework.org/schema/task");
                root.setAttribute(
                        "xsi:schemaLocation",
                        root.getAttribute("xsi:schemaLocation")
                                + "  http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd");
            }
            root.appendChild(new XmlElementBuilder("task:annotation-driven",
                    appContext).addAttribute("executor", "asyncExecutor")
                    .build());
            root.appendChild(new XmlElementBuilder("task:executor", appContext)
                    .addAttribute("id", "asyncExecutor")
                    .addAttribute("pool-size", "${executor.poolSize}").build());

            fileManager.createOrUpdateTextFileIfRequired(contextPath,
                    XmlUtils.nodeToString(appContext), false);

            propFileOperations.addPropertyIfNotExists(Path.SPRING_CONFIG_ROOT
                    .getModulePathId(projectOperations.getFocusedModuleName()),
                    "jms.properties", "executor.poolSize", "10", true);
        }
    }

    private String getPhysicalLocationCanonicalPath(
            final String physicalTypeIdentifier) {
        Validate.isTrue(PhysicalTypeIdentifier.isValid(physicalTypeIdentifier),
                "Physical type identifier is invalid");
        final JavaType javaType = PhysicalTypeIdentifier
                .getJavaType(physicalTypeIdentifier);
        final LogicalPath path = PhysicalTypeIdentifier
                .getPath(physicalTypeIdentifier);
        return projectOperations.getPathResolver().getIdentifier(path,
                javaType.getRelativeFileName());
    }

    private boolean hasJmsContext() {
        return fileManager.exists(projectOperations.getPathResolver()
                .getFocusedIdentifier(Path.SPRING_CONFIG_ROOT,
                        "applicationContext-jms.xml"));
    }

    public void injectJmsTemplate(final JavaType targetType,
            final JavaSymbolName fieldName, final boolean asynchronous) {
        Validate.notNull(targetType, "Java type required");
        Validate.notNull(fieldName, "Field name required");

        final ClassOrInterfaceTypeDetails targetTypeDetails = typeLocationService
                .getTypeDetails(targetType);
        Validate.isTrue(targetTypeDetails != null,
                "Cannot locate source for '%s'",
                targetType.getFullyQualifiedTypeName());

        final String declaredByMetadataId = targetTypeDetails
                .getDeclaredByMetadataId();
        final ClassOrInterfaceTypeDetailsBuilder cidBuilder = new ClassOrInterfaceTypeDetailsBuilder(
                targetTypeDetails);

        // Create the field
        cidBuilder.addField(new FieldMetadataBuilder(declaredByMetadataId,
                PRIVATE | TRANSIENT, Arrays
                        .asList(new AnnotationMetadataBuilder(AUTOWIRED)),
                fieldName, JMS_OPERATIONS));

        // Create the method
        cidBuilder.addMethod(createSendMessageMethod(fieldName,
                declaredByMetadataId, asynchronous));

        if (asynchronous) {
            ensureSpringAsynchronousSupportEnabled();
        }

        typeManagementService.createOrUpdateTypeOnDisk(cidBuilder.build());
    }

    public void installJms(final JmsProvider jmsProvider, final String name,
            final JmsDestinationType destinationType) {
        Validate.isTrue(isJmsInstallationPossible(), "Project not available");
        Validate.notNull(jmsProvider, "JMS provider required");

        final String jmsContextPath = projectOperations.getPathResolver()
                .getFocusedIdentifier(Path.SPRING_CONFIG_ROOT,
                        "applicationContext-jms.xml");

        String amq;
        String destType;
        switch (destinationType) {
        case TOPIC:
            amq = destType = "topic";
            break;
        case DURABLE_TOPIC:
            amq = "topic";
            destType = "durableTopic";
            break;
        default:
            amq = destType = "queue";
            break;
        }

        final InputStream in;
        if (fileManager.exists(jmsContextPath)) {
            in = fileManager.getInputStream(jmsContextPath);
        }
        else {
            in = FileUtils.getInputStream(getClass(),
                    "applicationContext-jms-template.xml");
            Validate.notNull(in,
                    "Could not acquire applicationContext-jms.xml template");
        }
        final Document document = XmlUtils.readXml(in);

        final Element root = document.getDocumentElement();

        if (StringUtils.isNotBlank(name)) {
            final Element destination = document.createElement("amq:" + amq);
            destination.setAttribute("physicalName", name);
            destination.setAttribute("id", name);
            root.appendChild(destination);
            addDefaultDestination(document, name);
        }

        Element listenerContainer = XmlUtils.findFirstElement(
                "/beans/listener-container[@destination-type = '" + destType
                        + "']", root);
        if (listenerContainer == null) {
            listenerContainer = document
                    .createElement("jms:listener-container");
            listenerContainer.setAttribute("connection-factory", "jmsFactory");
            listenerContainer.setAttribute("destination-type", destType);
            root.appendChild(listenerContainer);
        }

        DomUtils.removeTextNodes(root);

        fileManager.createOrUpdateTextFileIfRequired(jmsContextPath,
                XmlUtils.nodeToString(document), false);

        updateConfiguration(jmsProvider);
    }

    public boolean isJmsInstallationPossible() {
        return projectOperations.isFocusedProjectAvailable();
    }

    public boolean isManageJmsAvailable() {
        return projectOperations.isFocusedProjectAvailable() && hasJmsContext();
    }

    private void updateConfiguration(final JmsProvider jmsProvider) {
        final Element configuration = XmlUtils.getConfiguration(getClass());

        final List<Dependency> dependencies = new ArrayList<Dependency>();

        final List<Element> springDependencies = XmlUtils.findElements(
                "/configuration/springJms/dependencies/dependency",
                configuration);
        for (final Element dependencyElement : springDependencies) {
            dependencies.add(new Dependency(dependencyElement));
        }

        final List<Element> jmsDependencies = XmlUtils.findElements(
                "/configuration/jmsProviders/provider[@id = '"
                        + jmsProvider.name() + "']/dependencies/dependency",
                configuration);
        for (final Element dependencyElement : jmsDependencies) {
            dependencies.add(new Dependency(dependencyElement));
        }

        projectOperations.addDependencies(
                projectOperations.getFocusedModuleName(), dependencies);
    }
}
TOP

Related Classes of org.springframework.roo.addon.jms.JmsOperationsImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.