All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.modules.spring.webmvc.SpringWebModuleExtender Maven / Gradle / Ivy

There is a newer version: RELEASE240
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * Contributor(s): Craig MacKay
 */

package org.netbeans.modules.spring.webmvc;

import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.project.classpath.ProjectClassPathModifier;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.SourceGroup;
import org.netbeans.api.project.libraries.Library;
import org.netbeans.modules.j2ee.common.dd.DDHelper;
import org.netbeans.modules.j2ee.core.api.support.SourceGroups;
import org.netbeans.modules.j2ee.dd.api.common.CommonDDBean;
import org.netbeans.modules.j2ee.dd.api.common.CreateCapability;
import org.netbeans.modules.j2ee.dd.api.common.InitParam;
import org.netbeans.modules.j2ee.dd.api.web.DDProvider;
import org.netbeans.modules.j2ee.dd.api.web.Listener;
import org.netbeans.modules.j2ee.dd.api.web.Servlet;
import org.netbeans.modules.j2ee.dd.api.web.ServletMapping;
import org.netbeans.modules.j2ee.dd.api.web.ServletMapping25;
import org.netbeans.modules.j2ee.dd.api.web.WebApp;
import org.netbeans.modules.j2ee.dd.api.web.WelcomeFileList;
import org.netbeans.modules.spring.api.SpringUtilities;
import org.netbeans.modules.spring.api.beans.ConfigFileGroup;
import org.netbeans.modules.spring.api.beans.ConfigFileManager;
import org.netbeans.modules.spring.api.beans.SpringScope;
import org.netbeans.modules.spring.webmvc.utils.SpringWebFrameworkUtils;
import org.netbeans.modules.web.api.webmodule.ExtenderController;
import org.netbeans.modules.web.api.webmodule.WebModule;
import org.netbeans.modules.web.spi.webmodule.WebModuleExtender;
import org.openide.DialogDisplayer;
import org.openide.ErrorManager;
import org.openide.NotifyDescriptor.Message;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileSystem;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataFolder;
import org.openide.loaders.DataObject;
import org.openide.util.ChangeSupport;
import org.openide.util.Exceptions;
import org.openide.util.HelpCtx;
import org.openide.util.Mutex.ExceptionAction;
import org.openide.util.MutexException;
import org.openide.util.NbBundle;

/**
 * The WebModuleExtender implementation for Spring Web MVC.
 *
 * @author Craig MacKay et al.
 */

public class SpringWebModuleExtender extends WebModuleExtender implements ChangeListener {
    private static final Logger LOGGER = Logger.getLogger(SpringWebModuleExtender.class.getName());

    private final ChangeSupport changeSupport = new ChangeSupport(this);
    private final SpringWebFrameworkProvider framework;
    private final ExtenderController controller;
    private final boolean customizer;
    private final WebModule webModule;
    private SpringConfigPanelVisual component;
    private String dispatcherName = "dispatcher"; // NOI18N
    private String dispatcherMapping = "*.htm"; // NOI18N
    private boolean includeJstl = true;

    /**
     * Creates a new instance of SpringWebModuleExtender
     * @param framework
     * @param controller an instance of org.netbeans.modules.web.api.webmodule.ExtenderController
     * @param webModule the web module to extend, or {@code null} for new projects.
     * @param customizer
     */
    public SpringWebModuleExtender(SpringWebFrameworkProvider framework, ExtenderController controller, WebModule webModule, boolean customizer) {
        this.framework = framework;
        this.controller = controller;
        this.webModule = webModule;
        this.customizer = customizer;
    }

    public ExtenderController getController() {
        return controller;
    }

    public String getDispatcherName() {
        return dispatcherName;
    }

    public String getDispatcherMapping() {
        return dispatcherMapping;
    }

    public boolean getIncludeJstl() {
        return includeJstl;
    }

    public synchronized SpringConfigPanelVisual getComponent() {
        if (component == null) {
            component = new SpringConfigPanelVisual(this);
            component.setEnabled(!customizer);
        }
        return component;
    }

    public boolean isValid() {
        if (dispatcherName == null || dispatcherName.trim().length() == 0){
            controller.setErrorMessage(NbBundle.getMessage(SpringConfigPanelVisual.class, "MSG_DispatcherNameIsEmpty")); // NOI18N
            return false;
        }

        if (!SpringWebFrameworkUtils.isDispatcherServletConfigFilenameValid(dispatcherName)){
            controller.setErrorMessage(NbBundle.getMessage(SpringConfigPanelVisual.class, "MSG_DispatcherServletConfigFilenameIsNotValid"));
            return false;
        }

        if (dispatcherMapping == null || dispatcherMapping.trim().length() == 0) {
            controller.setErrorMessage(NbBundle.getMessage(SpringConfigPanelVisual.class, "MSG_DispatcherMappingPatternIsEmpty")); // NOI18N
            return false;
        }
        if (!SpringWebFrameworkUtils.isDispatcherMappingPatternValid(dispatcherMapping)){
            controller.setErrorMessage(NbBundle.getMessage(SpringConfigPanelVisual.class, "MSG_DispatcherMappingPatternIsNotValid")); // NOI18N
            return false;
        }
        if (webModule != null && !isWebXmlValid(webModule)) {
            controller.setErrorMessage(NbBundle.getMessage(SpringConfigPanelVisual.class, "MSG_WebXmlIsNotValid")); // NOI18N
            return false;
        }
        if (getComponent().getSpringLibrary() == null) {
            controller.setErrorMessage(NbBundle.getMessage(SpringConfigPanelVisual.class, "MSG_NoValidSpringLibraryFound")); // NOI18N
            return false;
        }

        controller.setErrorMessage(null);
        return true;
    }

    public HelpCtx getHelp() {
        return new HelpCtx(SpringWebModuleExtender.class);
    }

    public final void addChangeListener(ChangeListener l) {
        changeSupport.addChangeListener(l);
    }

    public final void removeChangeListener(ChangeListener l) {
        changeSupport.removeChangeListener(l);
    }

    public void stateChanged(ChangeEvent e) {
        dispatcherName = getComponent().getDispatcherName();
        dispatcherMapping = getComponent().getDispatcherMapping();
        includeJstl = getComponent().getIncludeJstl();
        changeSupport.fireChange();
    }

    @Override
    public void update() {
    // not used yet
    }

    @Override
    public Set extend(WebModule webModule) {
        CreateSpringConfig createSpringConfig = new CreateSpringConfig(webModule);
        FileObject webInf = webModule.getWebInf();
        if (webInf == null) {
            try {
                FileObject documentBase = webModule.getDocumentBase();
                if (documentBase == null) {
                    LOGGER.log(Level.INFO, "WebModule does not have valid documentBase");
                    return Collections.emptySet();
                }
                webInf = FileUtil.createFolder(documentBase, "WEB-INF"); //NOI18N
            } catch (IOException ex) {
                LOGGER.log(Level.WARNING, "Exception during creating WEB-INF directory", ex);
            }
        }
        if (webInf != null) {
            try {
                FileSystem fs = webInf.getFileSystem();
                fs.runAtomicAction(createSpringConfig);
            } catch (IOException e) {
                Logger.getLogger("global").log(Level.INFO, null, e);
                return Collections.emptySet();
            }
        }
        return createSpringConfig.getFilesToOpen();
    }

    private boolean isWebXmlValid(WebModule webModule) {
        FileObject webXml = webModule.getDeploymentDescriptor();
        if (webXml == null) {
            return true;
        }
        WebApp webApp = null;
        try {
            webApp = DDProvider.getDefault().getDDRoot(webXml);
        } catch (IOException ex) {
            LOGGER.log(Level.INFO, "Can't read web.xml file: " + webXml.getPath(), ex);
        }
        return webApp != null && webApp.getStatus() == WebApp.STATE_VALID;
    }

    private class CreateSpringConfig implements FileSystem.AtomicAction {

        public static final String CONTEXT_LOADER = "org.springframework.web.context.ContextLoaderListener"; // NOI18N
        public static final String DISPATCHER_SERVLET = "org.springframework.web.servlet.DispatcherServlet"; // NOI18N
        public static final String ENCODING = "UTF-8"; // NOI18N
        
        private final Set filesToOpen = new LinkedHashSet();
        private final WebModule webModule;

        public CreateSpringConfig(WebModule webModule) {
            this.webModule = webModule;
        }

        @NbBundle.Messages({
            "CreateSpringConfig.msg.invalid.dd=Deployment descriptor cointains errors, Spring framework has to be manually configured there!"
        })
        public void run() throws IOException {
            // MODIFY WEB.XML
            FileObject dd = webModule.getDeploymentDescriptor();
            //we need deployment descriptor, create if null
            if(dd==null)
            {
                dd = DDHelper.createWebXml(webModule.getJ2eeProfile(), webModule.getWebInf());
            }
            if (dd != null) {
                WebApp ddRoot = DDProvider.getDefault().getDDRoot(dd);
                if (ddRoot.getError() != null) {
                    Message message = new Message(Bundle.CreateSpringConfig_msg_invalid_dd(), Message.ERROR_MESSAGE);
                    DialogDisplayer.getDefault().notifyLater(message);
                } else {
                    addContextParam(ddRoot, "contextConfigLocation", "/WEB-INF/applicationContext.xml"); // NOI18N
                    addListener(ddRoot, CONTEXT_LOADER);
                    addServlet(ddRoot, getComponent().getDispatcherName(), DISPATCHER_SERVLET, getComponent().getDispatcherMapping(), "2"); // NOI18N
                    WelcomeFileList welcomeFiles = ddRoot.getSingleWelcomeFileList();
                    if (welcomeFiles == null) {
                        try {
                            welcomeFiles = (WelcomeFileList) ddRoot.createBean("WelcomeFileList"); // NOI18N
                            ddRoot.setWelcomeFileList(welcomeFiles);
                        } catch (ClassNotFoundException ex) {
                            Exceptions.printStackTrace(ex);
                        }
                    }
                    if (welcomeFiles.sizeWelcomeFile() == 0) {
                        welcomeFiles.addWelcomeFile("redirect.jsp"); // NOI18N
                    }
                    ddRoot.write(dd);
                }
            }
            
            // ADD JSTL LIBRARY IF ENABLED AND SPRING LIBRARY
            List libraries = new ArrayList(3);
            Library springLibrary = component.getSpringLibrary();
            String version = component.getSpringLibraryVersion();
            Library webMVCLibrary = null;
            if (springLibrary != null) {
                libraries.add(springLibrary);
                if (SpringUtilities.isSpringWebMVCLibrary(springLibrary)) {
                    webMVCLibrary = springLibrary;
                }
            } else {
                LOGGER.log(Level.WARNING, null, new Error("No Spring Framework library found."));
            }
            if (webMVCLibrary == null) {
                webMVCLibrary = SpringUtilities.findSpringWebMVCLibrary(version);
                if (webMVCLibrary !=null) {
                    libraries.add(webMVCLibrary);
                } else {
                    LOGGER.log(Level.WARNING, null, new Error("No Spring Web MVC library with version "+version+" found."));
                }
            }
//            Library webMVCLibrary = SpringUtilities.findSpringWebMVCLibrary();
//            Library springLibrary = null;
//            if (webMVCLibrary != null) {
//                libraries.add(webMVCLibrary);
//                if (SpringUtilities.isSpringLibrary(webMVCLibrary)) {
//                    // In case this is an user library with a monolithic Spring.
//                    springLibrary = webMVCLibrary;
//                }
//            } else {
//                LOGGER.log(Level.WARNING, null, new Error("No Spring Web MVC library found."));
//            }
//            if (springLibrary == null) {
//                springLibrary = SpringUtilities.findSpringLibrary();
//                if (springLibrary != null){
//                    libraries.add(springLibrary);
//                } else {
//                    LOGGER.log(Level.WARNING, null, new Error("No Spring Framework library found."));
//                }
//            }
            if (includeJstl) {
                Library jstlLibrary = SpringUtilities.findJSTLibrary();
                if (jstlLibrary != null) {
                    libraries.add(jstlLibrary);
                } else {
                    LOGGER.log(Level.WARNING, null, new Error("No JSTL library found."));
                }
            }
            if (!libraries.isEmpty()) {
                addLibrariesToWebModule(libraries, webModule);
            }

            // CREATE WEB-INF/JSP FOLDER
            FileObject webInf = webModule.getWebInf();

            FileObject jsp = FileUtil.createFolder(webInf, "jsp");
            // COPY TEMPLATE SPRING RESOURCES (JSP, XML, PROPERTIES)
            DataFolder webInfDO = DataFolder.findFolder(webInf);
            final List newConfigFiles = new ArrayList<>(2);
            HashMap params = new HashMap<>();
            
            String appContextTemplateName = "applicationContext-4.xml"; //NOI18N
            String dispServletTemplateName = "dispatcher-servlet-4.xml"; //NOI18N
            if (version.startsWith("3.")) {    //NOI18N
                appContextTemplateName = "applicationContext-3.xml"; //NOI18N
                dispServletTemplateName = "dispatcher-servlet-3.xml"; //NOI18N
            }
            
            FileObject configFile = createFromTemplate(appContextTemplateName, webInfDO, "applicationContext",params); // NOI18N
            addFileToOpen(configFile);
            newConfigFiles.add(FileUtil.toFile(configFile));
            String fullIndexUrl = SpringWebFrameworkUtils.instantiateDispatcherMapping(dispatcherMapping, "index"); // NOI18N
            String simpleIndexUrl = SpringWebFrameworkUtils.getSimpleDispatcherURL(fullIndexUrl);
            Map indexUrlParams = Collections.singletonMap("index", Collections.singletonMap("url", simpleIndexUrl)); // NOI18N
            params.putAll(indexUrlParams);
            configFile = createFromTemplate(dispServletTemplateName, webInfDO, getComponent().getDispatcherName() + "-servlet", params); // NOI18N
            addFileToOpen(configFile);
            newConfigFiles.add(FileUtil.toFile(configFile));
            addFileToOpen(createFromTemplate("index.jsp", DataFolder.findFolder(jsp), "index")); // NOI18N

            // MODIFY EXISTING REDIRECT.JSP
            indexUrlParams = Collections.singletonMap("index", Collections.singletonMap("url", fullIndexUrl)); // NOI18N
            FileObject documentBase = webModule.getDocumentBase();
            FileObject redirectJsp = documentBase.getFileObject("redirect.jsp"); // NOI18N
            if (redirectJsp != null) {
                redirectJsp.delete();
            }
            DataFolder documentBaseDO = DataFolder.findFolder(documentBase);
            addFileToOpen(createFromTemplate("redirect.jsp", documentBaseDO, "redirect", indexUrlParams));

            SpringScope scope = SpringScope.getSpringScope(configFile);
            if (scope != null) {
                final ConfigFileManager manager = scope.getConfigFileManager();
                try {
                    manager.mutex().writeAccess(new ExceptionAction() {
                        public Void run() throws IOException {
                            List files = manager.getConfigFiles();
                            files.addAll(newConfigFiles);
                            List groups = manager.getConfigFileGroups();
                            String groupName = NbBundle.getMessage(SpringWebModuleExtender.class, "LBL_DefaultGroup");
                            ConfigFileGroup newGroup = ConfigFileGroup.create(groupName, newConfigFiles);
                            groups.add(newGroup);
                            manager.putConfigFilesAndGroups(files, groups);
                            manager.save();
                            return null;
                        }
                    });
                } catch (MutexException e) {
                    throw (IOException)e.getException();
                }
            } else {
                LOGGER.log(Level.WARNING, "Could not find a SpringScope for file {0}", configFile);
            }
        }

        public void addFileToOpen(FileObject file) {
            filesToOpen.add(file);
        }

        public Set getFilesToOpen() {
            return filesToOpen;
        }

        private FileObject createFromTemplate(String templateName, DataFolder targetDO, String fileName, Map params) throws IOException {
            FileObject templateFO = FileUtil.getConfigFile("SpringFramework/Templates/" + templateName);
            DataObject templateDO = DataObject.find(templateFO);
            return templateDO.createFromTemplate(targetDO, fileName, params).getPrimaryFile();
        }

        private FileObject createFromTemplate(String templateName, DataFolder targetDO, String fileName) throws IOException {
            FileObject templateFO = FileUtil.getConfigFile("SpringFramework/Templates/" + templateName);
            DataObject templateDO = DataObject.find(templateFO);
            return templateDO.createFromTemplate(targetDO, fileName).getPrimaryFile();
        }

        protected boolean addLibrariesToWebModule(List libraries, WebModule webModule) throws IOException, UnsupportedOperationException {
            FileObject fileObject = webModule.getDocumentBase();
            Project project = FileOwnerQuery.getOwner(fileObject);
            if (project == null) {
                return false;
            }
            boolean addLibraryResult = false;
            try {
                SourceGroup[] groups = SourceGroups.getJavaSourceGroups(project);
                if (groups.length == 0) {
                    return false;
                }
                addLibraryResult = ProjectClassPathModifier.addLibraries(libraries.toArray(new Library[libraries.size()]), groups[0].getRootFolder(), ClassPath.COMPILE);
            } catch (IOException e) {
                LOGGER.log(Level.WARNING, "Libraries required for the Spring MVC project not added", e); // NOI18N
            } catch (UnsupportedOperationException uoe) {
                LOGGER.log(Level.WARNING, "This project does not support adding these types of libraries to the classpath", uoe); // NOI18N
            }
            return addLibraryResult;
        }

        protected Listener addListener(WebApp webApp, String classname) throws IOException {
            Listener listener = (Listener) createBean(webApp, "Listener"); // NOI18N
            listener.setListenerClass(classname);
            webApp.addListener(listener);
            return listener;
        }

        protected Servlet addServlet(WebApp webApp, String name, String classname, String pattern, String loadOnStartup) throws IOException {
            Servlet servlet = (Servlet) createBean(webApp, "Servlet"); // NOI18N
            servlet.setServletName(name);
            servlet.setServletClass(classname);
            if (loadOnStartup != null) {
                servlet.setLoadOnStartup(new BigInteger(loadOnStartup));
            }
            webApp.addServlet(servlet);
            if (pattern != null) {
                addServletMapping(webApp, name, pattern);
            }
            return servlet;
        }

        protected ServletMapping addServletMapping(WebApp webApp, String name, String pattern) throws IOException {
            ServletMapping25 mapping = (ServletMapping25) createBean(webApp, "ServletMapping"); // NOI18N
            mapping.setServletName(name);
            mapping.addUrlPattern(pattern);
            webApp.addServletMapping(mapping);
            return mapping;
        }

        protected InitParam addContextParam(WebApp webApp, String name, String value) throws IOException {
            InitParam initParam = (InitParam) createBean(webApp, "InitParam"); // NOI18N
            initParam.setParamName(name);
            initParam.setParamValue(value);
            webApp.addContextParam(initParam);
            return initParam;
        }

        protected CommonDDBean createBean(CreateCapability creator, String beanName) throws IOException {
            CommonDDBean bean = null;
            try {
                bean = creator.createBean(beanName);
            } catch (ClassNotFoundException ex) {
                ErrorManager.getDefault().notify(ErrorManager.EXCEPTION, ex);
                throw new IOException("Error creating bean with name:" + beanName); // NOI18N
            }
            return bean;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy