package org.springframework.roo.addon.web.flow; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; 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.web.flow.XmlTemplate.DomElementCallback; import org.springframework.roo.addon.web.mvc.controller.WebMvcOperations; import org.springframework.roo.addon.web.mvc.jsp.JspOperations; import org.springframework.roo.addon.web.mvc.jsp.menu.MenuOperations; import org.springframework.roo.addon.web.mvc.jsp.tiles.TilesOperations; import org.springframework.roo.model.JavaSymbolName; import org.springframework.roo.process.manager.FileManager; import org.springframework.roo.project.Dependency; import org.springframework.roo.project.FeatureNames; import org.springframework.roo.project.Path; import org.springframework.roo.project.PathResolver; import org.springframework.roo.project.ProjectOperations; import org.springframework.roo.project.ProjectType; import org.springframework.roo.project.Repository; import org.springframework.roo.support.util.FileUtils; import org.springframework.roo.support.util.XmlUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Provides Web Flow configuration operations. * * @author Stefan Schmidt * @author Rossen Stoyanchev * @since 1.0 */ @Component @Service public class WebFlowOperationsImpl implements WebFlowOperations { @Reference private FileManager fileManager; @Reference private JspOperations jspOperations; @Reference private MenuOperations menuOperations; @Reference private PathResolver pathResolver; @Reference private ProjectOperations projectOperations; @Reference private TilesOperations tilesOperations; @Reference private WebMvcOperations webMvcOperations; private void copyTemplate(final String templateFileName, final String resolvedTargetDirectoryPath) { InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = FileUtils .getInputStream(getClass(), templateFileName); outputStream = fileManager.createFile( resolvedTargetDirectoryPath + "/" + templateFileName) .getOutputStream(); IOUtils.copy(inputStream, outputStream); } catch (final IOException e) { throw new IllegalStateException( "Encountered an error during copying of resources for Web Flow addon.", e); } finally { IOUtils.closeQuietly(inputStream); IOUtils.closeQuietly(outputStream); } } private String getFlowId(String flowName) { flowName = StringUtils.defaultIfEmpty(flowName, "sample-flow"); if (flowName.startsWith("/")) { flowName = flowName.substring(1); } return flowName.replaceAll("[^a-zA-Z/_]", ""); } /** * See {@link WebFlowOperations#installWebFlow(String)}. */ public void installWebFlow(final String flowName) { installWebFlowConfiguration(); final String flowId = getFlowId(flowName); final String webRelativeFlowPath = "/WEB-INF/views/" + flowId; final String resolvedFlowPath = pathResolver.getFocusedIdentifier( Path.SRC_MAIN_WEBAPP, webRelativeFlowPath); final String resolvedFlowDefinitionPath = resolvedFlowPath + "/flow.xml"; if (fileManager.exists(resolvedFlowPath)) { throw new IllegalStateException("Flow directory already exists: " + resolvedFlowPath); } fileManager.createDirectory(resolvedFlowPath); copyTemplate("flow.xml", resolvedFlowPath); copyTemplate("view-state-1.jspx", resolvedFlowPath); copyTemplate("view-state-2.jspx", resolvedFlowPath); copyTemplate("end-state.jspx", resolvedFlowPath); new XmlTemplate(fileManager).update(resolvedFlowDefinitionPath, new DomElementCallback() { public boolean doWithElement(final Document document, final Element root) { final List<Element> states = XmlUtils.findElements( "/flow/view-state|end-state", root); for (final Element state : states) { state.setAttribute("view", flowId + "/" + state.getAttribute("id")); } return true; } }); final JavaSymbolName flowMenuCategory = new JavaSymbolName("Flows"); final JavaSymbolName flowMenuName = new JavaSymbolName(flowId.replace( "/", "_")); menuOperations.addMenuItem(flowMenuCategory, flowMenuName, flowMenuName.getReadableSymbolName(), "webflow_menu_enter", "/" + flowId, null, pathResolver.getFocusedPath(Path.SRC_MAIN_WEBAPP)); tilesOperations.addViewDefinition(flowId, pathResolver.getFocusedPath(Path.SRC_MAIN_WEBAPP), flowId + "/*", TilesOperations.DEFAULT_TEMPLATE, webRelativeFlowPath + "/{1}.jspx"); updateConfiguration(); webMvcOperations.registerWebFlowConversionServiceExposingInterceptor(); } private void installWebFlowConfiguration() { final String resolvedSpringConfigPath = pathResolver .getFocusedIdentifier(Path.SRC_MAIN_WEBAPP, "WEB-INF/spring"); if (fileManager .exists(resolvedSpringConfigPath + "/webflow-config.xml")) { return; } copyTemplate("webflow-config.xml", resolvedSpringConfigPath); final String webMvcConfigPath = resolvedSpringConfigPath + "/webmvc-config.xml"; if (!fileManager.exists(webMvcConfigPath)) { webMvcOperations.installAllWebMvcArtifacts(); } jspOperations.installCommonViewArtefacts(); new XmlTemplate(fileManager).update(webMvcConfigPath, new DomElementCallback() { public boolean doWithElement(final Document document, final Element root) { if (null == XmlUtils .findFirstElement( "/beans/import[@resource='webflow-config.xml']", root)) { final Element importSWF = document .createElement("import"); importSWF.setAttribute("resource", "webflow-config.xml"); root.appendChild(importSWF); return true; } return false; } }); } public boolean isWebFlowInstallationPossible() { return projectOperations .isFeatureInstalledInFocusedModule(FeatureNames.MVC) && !projectOperations .isFeatureInstalledInFocusedModule(FeatureNames.JSF); } private void updateConfiguration() { final Element configuration = XmlUtils.getConfiguration(getClass()); final String focusedModuleName = projectOperations .getFocusedModuleName(); final List<Dependency> dependencyElements = new ArrayList<Dependency>(); for (final Element webFlowDependencyElement : XmlUtils.findElements( "/configuration/springWebFlow/dependencies/dependency", configuration)) { dependencyElements.add(new Dependency(webFlowDependencyElement)); } projectOperations .addDependencies(focusedModuleName, dependencyElements); final List<Repository> repositoryElements = new ArrayList<Repository>(); for (final Element repositoryElement : XmlUtils.findElements( "/configuration/springWebFlow/repositories/repository", configuration)) { repositoryElements.add(new Repository(repositoryElement)); } projectOperations .addRepositories(focusedModuleName, repositoryElements); projectOperations.updateProjectType(focusedModuleName, ProjectType.WAR); } }