package org.springframework.roo.addon.gwt; import static java.lang.reflect.Modifier.PUBLIC; import static org.springframework.roo.addon.gwt.GwtJavaType.ENTITY_PROXY; import static org.springframework.roo.addon.gwt.GwtJavaType.OLD_ENTITY_PROXY; import static org.springframework.roo.addon.gwt.GwtJavaType.OLD_REQUEST_CONTEXT; import static org.springframework.roo.addon.gwt.GwtJavaType.PROXY_FOR_NAME; import static org.springframework.roo.addon.gwt.GwtJavaType.REQUEST_CONTEXT; import static org.springframework.roo.classpath.PhysicalTypeCategory.INTERFACE; import static org.springframework.roo.model.RooJavaType.ROO_GWT_MIRRORED_FROM; import static org.springframework.roo.model.RooJavaType.ROO_GWT_PROXY; import static org.springframework.roo.model.RooJavaType.ROO_GWT_REQUEST; import static org.springframework.roo.model.RooJavaType.ROO_GWT_UNMANAGED_REQUEST; import static org.springframework.roo.model.RooJavaType.ROO_JPA_ACTIVE_RECORD; import static org.springframework.roo.model.RooJavaType.ROO_JPA_ENTITY; import static org.springframework.roo.project.Path.ROOT; import static org.springframework.roo.project.Path.SRC_MAIN_JAVA; import static org.springframework.roo.project.Path.SRC_MAIN_WEBAPP; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import org.apache.commons.io.IOUtils; 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.osgi.service.component.ComponentContext; import org.springframework.roo.addon.gwt.request.GwtRequestMetadata; import org.springframework.roo.addon.web.mvc.controller.WebMvcOperations; 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.FieldMetadata; import org.springframework.roo.classpath.details.MemberFindingUtils; import org.springframework.roo.classpath.details.annotations.AnnotationAttributeValue; import org.springframework.roo.classpath.details.annotations.AnnotationMetadata; import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder; import org.springframework.roo.classpath.details.annotations.ArrayAttributeValue; import org.springframework.roo.classpath.details.annotations.StringAttributeValue; import org.springframework.roo.classpath.persistence.PersistenceMemberLocator; import org.springframework.roo.file.monitor.event.FileDetails; import org.springframework.roo.metadata.MetadataService; import org.springframework.roo.model.JavaPackage; 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.FeatureNames; import org.springframework.roo.project.LogicalPath; import org.springframework.roo.project.Plugin; import org.springframework.roo.project.ProjectOperations; import org.springframework.roo.project.Repository; import org.springframework.roo.project.maven.Pom; import org.springframework.roo.support.osgi.OSGiUtils; import org.springframework.roo.support.util.CollectionUtils; import org.springframework.roo.support.util.DomUtils; import org.springframework.roo.support.util.FileUtils; import org.springframework.roo.support.util.WebXmlUtils; import org.springframework.roo.support.util.XmlElementBuilder; import org.springframework.roo.support.util.XmlUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Implementation of {@link GwtOperations}. * * @author Ben Alex * @author Alan Stewart * @author Stefan Schmidt * @author Ray Cromwell * @author Amit Manjhi * @since 1.1 */ @Component @Service public class GwtOperationsImpl implements GwtOperations { private static final String GWT_BUILD_COMMAND = "com.google.gwt.eclipse.core.gwtProjectValidator"; private static final String GWT_PROJECT_NATURE = "com.google.gwt.eclipse.core.gwtNature"; private static final String MAVEN_ECLIPSE_PLUGIN = "/project/build/plugins/plugin[artifactId = 'maven-eclipse-plugin']"; private static final String OUTPUT_DIRECTORY = "${project.build.directory}/${project.build.finalName}/WEB-INF/classes"; private static final JavaSymbolName VALUE = new JavaSymbolName("value"); @Reference protected FileManager fileManager; @Reference protected GwtTemplateService gwtTemplateService; @Reference protected GwtTypeService gwtTypeService; @Reference protected MetadataService metadataService; @Reference protected PersistenceMemberLocator persistenceMemberLocator; @Reference protected ProjectOperations projectOperations; @Reference protected TypeLocationService typeLocationService; @Reference protected TypeManagementService typeManagementService; @Reference protected WebMvcOperations webMvcOperations; private Boolean wasGaeEnabled; private ComponentContext context; protected void activate(final ComponentContext context) { this.context = context; } private void addPackageToGwtXml(final JavaPackage sourcePackage) { String gwtConfig = gwtTypeService.getGwtModuleXml(projectOperations .getFocusedModuleName()); gwtConfig = StringUtils.stripEnd(gwtConfig, File.separator); final String moduleRoot = projectOperations.getPathResolver() .getFocusedRoot(SRC_MAIN_JAVA); final String topLevelPackage = gwtConfig.replace( FileUtils.ensureTrailingSeparator(moduleRoot), "").replace( File.separator, "."); final String relativePackage = StringUtils.removeStart( sourcePackage.getFullyQualifiedPackageName(), topLevelPackage + "."); gwtTypeService.addSourcePath( relativePackage.replace(".", PATH_DELIMITER), projectOperations.getFocusedModuleName()); } private void copyDirectoryContents() { for (final GwtPath path : GwtPath.values()) { copyDirectoryContents(path); } } private void copyDirectoryContents(final GwtPath gwtPath) { final String sourceAntPath = gwtPath.getSourceAntPath(); if (sourceAntPath.contains("gae") && !projectOperations .isFeatureInstalledInFocusedModule(FeatureNames.GAE)) { return; } final String targetDirectory = gwtPath == GwtPath.WEB ? projectOperations .getPathResolver().getFocusedRoot(SRC_MAIN_WEBAPP) : projectOperations.getPathResolver().getFocusedIdentifier( SRC_MAIN_JAVA, gwtPath.getPackagePath(projectOperations .getFocusedTopLevelPackage())); updateFile(sourceAntPath, targetDirectory, gwtPath.segmentPackage(), false); } private void createProxy(final ClassOrInterfaceTypeDetails entity, final JavaPackage destinationPackage) { final ClassOrInterfaceTypeDetails existingProxy = gwtTypeService .lookupProxyFromEntity(entity); if (existingProxy != null || entity.isAbstract()) { return; } final JavaType proxyType = new JavaType( destinationPackage.getFullyQualifiedPackageName() + "." + entity.getName().getSimpleTypeName() + "Proxy"); final String focusedModule = projectOperations.getFocusedModuleName(); final LogicalPath proxyLogicalPath = LogicalPath.getInstance( SRC_MAIN_JAVA, focusedModule); final ClassOrInterfaceTypeDetailsBuilder cidBuilder = new ClassOrInterfaceTypeDetailsBuilder( PhysicalTypeIdentifier.createIdentifier(proxyType, proxyLogicalPath)); cidBuilder.setName(proxyType); cidBuilder.setExtendsTypes(Collections.singletonList(ENTITY_PROXY)); cidBuilder.setPhysicalTypeCategory(INTERFACE); cidBuilder.setModifier(PUBLIC); final List<AnnotationAttributeValue<?>> attributeValues = new ArrayList<AnnotationAttributeValue<?>>(); final StringAttributeValue stringAttributeValue = new StringAttributeValue( VALUE, entity.getName().getFullyQualifiedTypeName()); attributeValues.add(stringAttributeValue); final String locator = projectOperations .getTopLevelPackage(focusedModule) + ".server.locator." + entity.getName().getSimpleTypeName() + "Locator"; final StringAttributeValue locatorAttributeValue = new StringAttributeValue( new JavaSymbolName("locator"), locator); attributeValues.add(locatorAttributeValue); cidBuilder.updateTypeAnnotation(new AnnotationMetadataBuilder( PROXY_FOR_NAME, attributeValues)); attributeValues.remove(locatorAttributeValue); final List<StringAttributeValue> readOnlyValues = new ArrayList<StringAttributeValue>(); final FieldMetadata versionField = persistenceMemberLocator .getVersionField(entity.getName()); if (versionField != null) { readOnlyValues.add(new StringAttributeValue(VALUE, versionField .getFieldName().getSymbolName())); } final List<FieldMetadata> idFields = persistenceMemberLocator .getIdentifierFields(entity.getName()); if (!CollectionUtils.isEmpty(idFields)) { readOnlyValues.add(new StringAttributeValue(VALUE, idFields.get(0) .getFieldName().getSymbolName())); } final ArrayAttributeValue<StringAttributeValue> readOnlyAttribute = new ArrayAttributeValue<StringAttributeValue>( new JavaSymbolName("readOnly"), readOnlyValues); attributeValues.add(readOnlyAttribute); cidBuilder.updateTypeAnnotation(new AnnotationMetadataBuilder( ROO_GWT_PROXY, attributeValues)); typeManagementService.createOrUpdateTypeOnDisk(cidBuilder.build()); addPackageToGwtXml(destinationPackage); } /** * Builds the given entity's managed RequestContext interface. Note that we * don't generate the entire interface here, only the @RooGwtRequest * annotation; we then invoke the metadata provider, which takes over and * generates the remaining code, namely the method declarations and the @ServiceName * annotation. This is analogous to how ITD-based addons work, e.g. adding a * trigger annotation and letting the metadata provider do the rest. This * allows for the metadata provider to correctly respond to project changes. * * @param entity the entity for which to create the GWT request interface * (required) * @param destinationPackage the package in which to create the request * interface (required) */ private void createRequestInterface( final ClassOrInterfaceTypeDetails entity, final JavaPackage destinationPackage) { final JavaType requestType = new JavaType( destinationPackage.getFullyQualifiedPackageName() + "." + entity.getType().getSimpleTypeName() + "Request_Roo_Gwt"); final LogicalPath focusedSrcMainJava = LogicalPath.getInstance( SRC_MAIN_JAVA, projectOperations.getFocusedModuleName()); final ClassOrInterfaceTypeDetailsBuilder requestBuilder = new ClassOrInterfaceTypeDetailsBuilder( PhysicalTypeIdentifier.createIdentifier(requestType, focusedSrcMainJava)); requestBuilder.setName(requestType); requestBuilder.addExtendsTypes(REQUEST_CONTEXT); requestBuilder.setPhysicalTypeCategory(INTERFACE); requestBuilder.setModifier(PUBLIC); requestBuilder.addAnnotation(getRooGwtRequestAnnotation(entity)); typeManagementService.createOrUpdateTypeOnDisk(requestBuilder.build()); addPackageToGwtXml(destinationPackage); // Trigger the GwtRequestMetadataProvider to finish generating the code metadataService.get(GwtRequestMetadata.createIdentifier(requestType, focusedSrcMainJava)); } /** * Builds the given entity's unmanaged RequestContext interface used for * adding custom methods. This interface extends the RequestContext * interface managed by Roo. * * @param entity the entity for which to create the GWT request interface * (required) * @param destinationPackage the package in which to create the request * interface (required) */ private void createUnmanagedRequestInterface( final ClassOrInterfaceTypeDetails entity, JavaPackage destinationPackage) { final ClassOrInterfaceTypeDetails managedRequest = gwtTypeService .lookupRequestFromEntity(entity); if (managedRequest == null) return; final JavaType unmanagedRequestType = new JavaType( destinationPackage.getFullyQualifiedPackageName() + "." + entity.getType().getSimpleTypeName() + "Request"); final LogicalPath focusedSrcMainJava = LogicalPath.getInstance( SRC_MAIN_JAVA, projectOperations.getFocusedModuleName()); final ClassOrInterfaceTypeDetailsBuilder unmanagedRequestBuilder = new ClassOrInterfaceTypeDetailsBuilder( PhysicalTypeIdentifier.createIdentifier(unmanagedRequestType, focusedSrcMainJava)); unmanagedRequestBuilder.setName(unmanagedRequestType); unmanagedRequestBuilder.addExtendsTypes(managedRequest.getType()); unmanagedRequestBuilder.setPhysicalTypeCategory(INTERFACE); unmanagedRequestBuilder.setModifier(PUBLIC); unmanagedRequestBuilder .addAnnotation(getRooGwtUnmanagedRequestAnnotation(entity)); unmanagedRequestBuilder.addAnnotation(managedRequest .getAnnotation(GwtJavaType.SERVICE_NAME)); typeManagementService.createOrUpdateTypeOnDisk(unmanagedRequestBuilder .build()); } private void createRequestInterfaceIfNecessary( final ClassOrInterfaceTypeDetails entity, final JavaPackage destinationPackage) { if (entity != null && !entity.isAbstract() && gwtTypeService.lookupRequestFromEntity(entity) == null) { createRequestInterface(entity, destinationPackage); createUnmanagedRequestInterface(entity, destinationPackage); } } private void createScaffold(final ClassOrInterfaceTypeDetails proxy) { final AnnotationMetadata annotationMetadata = GwtUtils .getFirstAnnotation(proxy, ROO_GWT_PROXY); if (annotationMetadata != null) { final AnnotationAttributeValue<Boolean> booleanAttributeValue = annotationMetadata .getAttribute("scaffold"); if (booleanAttributeValue == null || !booleanAttributeValue.getValue()) { final ClassOrInterfaceTypeDetailsBuilder cidBuilder = new ClassOrInterfaceTypeDetailsBuilder( proxy); final AnnotationMetadataBuilder annotationMetadataBuilder = new AnnotationMetadataBuilder( annotationMetadata); annotationMetadataBuilder.addBooleanAttribute("scaffold", true); for (final AnnotationMetadataBuilder existingAnnotation : cidBuilder .getAnnotations()) { if (existingAnnotation.getAnnotationType().equals( annotationMetadata.getAnnotationType())) { cidBuilder.getAnnotations().remove(existingAnnotation); cidBuilder.getAnnotations().add( annotationMetadataBuilder); break; } } typeManagementService.createOrUpdateTypeOnDisk(cidBuilder .build()); } } } private void deleteUntouchedSetupFiles(final String sourceAntPath, String targetDirectory) { if (!targetDirectory.endsWith(File.separator)) { targetDirectory += File.separator; } if (!fileManager.exists(targetDirectory)) { fileManager.createDirectory(targetDirectory); } final String path = FileUtils.getPath(getClass(), sourceAntPath); final Iterable<URL> uris = OSGiUtils.findEntriesByPattern( context.getBundleContext(), path); Validate.notNull(uris, "Could not search bundles for resources for Ant Path '" + path + "'"); for (final URL url : uris) { String fileName = url.getPath().substring( url.getPath().lastIndexOf('/') + 1); fileName = fileName.replace("-template", ""); final String targetFilename = targetDirectory + fileName; if (!fileManager.exists(targetFilename)) { continue; } try { String input = IOUtils.toString(url); input = processTemplate(input, null); final String existing = org.apache.commons.io.FileUtils .readFileToString(new File(targetFilename)); if (existing.equals(input)) { // new File(targetFilename).delete(); fileManager.delete(targetFilename); } } catch (final IOException ignored) { } } } private CharSequence getGaeHookup() { final StringBuilder builder = new StringBuilder( "// AppEngine user authentication\n\n"); builder.append("new GaeLoginWidgetDriver(requestFactory).setWidget(shell.getLoginWidget());\n\n"); builder.append("new ReloadOnAuthenticationFailure().register(eventBus);\n\n"); return builder.toString(); } public String getName() { return FeatureNames.GWT; } private String getPomPath() { return projectOperations.getPathResolver().getFocusedIdentifier(ROOT, "pom.xml"); } private AnnotationMetadata getRooGwtRequestAnnotation( final ClassOrInterfaceTypeDetails entity) { // The GwtRequestMetadataProvider doesn't need to know excluded methods // any more because it actively adds the required CRUD methods itself. final StringAttributeValue entityAttributeValue = new StringAttributeValue( VALUE, entity.getType().getFullyQualifiedTypeName()); final List<AnnotationAttributeValue<?>> gwtRequestAttributeValues = new ArrayList<AnnotationAttributeValue<?>>(); gwtRequestAttributeValues.add(entityAttributeValue); return new AnnotationMetadataBuilder(ROO_GWT_REQUEST, gwtRequestAttributeValues).build(); } private AnnotationMetadata getRooGwtUnmanagedRequestAnnotation( final ClassOrInterfaceTypeDetails entity) { final StringAttributeValue entityAttributeValue = new StringAttributeValue( VALUE, entity.getType().getFullyQualifiedTypeName()); final List<AnnotationAttributeValue<?>> gwtRequestAttributeValues = new ArrayList<AnnotationAttributeValue<?>>(); gwtRequestAttributeValues.add(entityAttributeValue); return new AnnotationMetadataBuilder(ROO_GWT_UNMANAGED_REQUEST, gwtRequestAttributeValues).build(); } public boolean isGwtInstallationPossible() { return projectOperations.isFocusedProjectAvailable() && !projectOperations .isFeatureInstalledInFocusedModule(FeatureNames.JSF); } public boolean isInstalledInModule(final String moduleName) { final Pom pom = projectOperations.getPomFromModuleName(moduleName); if (pom == null) { return false; } for (final Plugin buildPlugin : pom.getBuildPlugins()) { if ("gwt-maven-plugin".equals(buildPlugin.getArtifactId())) { return true; } } return false; } public boolean isScaffoldAvailable() { return isGwtInstallationPossible() && isInstalledInModule(projectOperations.getFocusedModuleName()); } private String processTemplate(String input, String segmentPackage) { if (segmentPackage == null) { segmentPackage = ""; } final String topLevelPackage = projectOperations.getTopLevelPackage( projectOperations.getFocusedModuleName()) .getFullyQualifiedPackageName(); input = input.replace("__TOP_LEVEL_PACKAGE__", topLevelPackage); input = input.replace("__SEGMENT_PACKAGE__", segmentPackage); input = input.replace("__PROJECT_NAME__", projectOperations .getProjectName(projectOperations.getFocusedModuleName())); if (projectOperations .isFeatureInstalledInFocusedModule(FeatureNames.GAE)) { input = input.replace("__GAE_IMPORT__", "import " + topLevelPackage + ".client.scaffold.gae.*;\n"); input = input.replace("__GAE_HOOKUP__", getGaeHookup()); input = input.replace("__GAE_REQUEST_TRANSPORT__", ", new GaeAuthRequestTransport(eventBus)"); } else { input = input.replace("__GAE_IMPORT__", ""); input = input.replace("__GAE_HOOKUP__", ""); input = input.replace("__GAE_REQUEST_TRANSPORT__", ""); } return input; } public void proxyAll(final JavaPackage proxyPackage) { for (final ClassOrInterfaceTypeDetails entity : typeLocationService .findClassesOrInterfaceDetailsWithAnnotation(ROO_JPA_ENTITY, ROO_JPA_ACTIVE_RECORD)) { createProxy(entity, proxyPackage); } copyDirectoryContents(GwtPath.LOCATOR); } public void proxyAndRequestAll(final JavaPackage proxyAndRequestPackage) { proxyAll(proxyAndRequestPackage); requestAll(proxyAndRequestPackage); } public void proxyAndRequestType(final JavaPackage proxyAndRequestPackage, final JavaType type) { proxyType(proxyAndRequestPackage, type); requestType(proxyAndRequestPackage, type); } public void proxyType(final JavaPackage proxyPackage, final JavaType type) { final ClassOrInterfaceTypeDetails entity = typeLocationService .getTypeDetails(type); if (entity != null) { createProxy(entity, proxyPackage); } copyDirectoryContents(GwtPath.LOCATOR); } private void removeIfFound(final String xpath, final Element webXmlRoot) { for (Element toRemove : XmlUtils.findElements(xpath, webXmlRoot)) { if (toRemove != null) { toRemove.getParentNode().removeChild(toRemove); toRemove = null; } } } public void requestAll(final JavaPackage proxyPackage) { for (final ClassOrInterfaceTypeDetails entity : typeLocationService .findClassesOrInterfaceDetailsWithAnnotation(ROO_JPA_ENTITY, ROO_JPA_ACTIVE_RECORD)) { createRequestInterfaceIfNecessary(entity, proxyPackage); } } public void requestType(final JavaPackage requestPackage, final JavaType type) { createRequestInterfaceIfNecessary( typeLocationService.getTypeDetails(type), requestPackage); } public void scaffoldAll(final JavaPackage proxyPackage, final JavaPackage requestPackage) { updateScaffoldBoilerPlate(); proxyAll(proxyPackage); requestAll(requestPackage); for (final ClassOrInterfaceTypeDetails proxy : typeLocationService .findClassesOrInterfaceDetailsWithAnnotation(ROO_GWT_PROXY)) { final ClassOrInterfaceTypeDetails request = gwtTypeService .lookupRequestFromProxy(proxy); if (request == null) { throw new IllegalStateException( "In order to scaffold, an entity must have a request"); } createScaffold(proxy); } } public void scaffoldType(final JavaPackage proxyPackage, final JavaPackage requestPackage, final JavaType type) { proxyType(proxyPackage, type); requestType(requestPackage, type); final ClassOrInterfaceTypeDetails entity = typeLocationService .getTypeDetails(type); if (entity != null && !entity.isAbstract()) { final ClassOrInterfaceTypeDetails proxy = gwtTypeService .lookupProxyFromEntity(entity); final ClassOrInterfaceTypeDetails request = gwtTypeService .lookupRequestFromEntity(entity); if (proxy == null || request == null) { throw new IllegalStateException( "In order to scaffold, an entity must have an associated proxy and request"); } updateScaffoldBoilerPlate(); createScaffold(proxy); } } public void setup() { // Install web pieces if not already installed if (!fileManager.exists(projectOperations.getPathResolver() .getFocusedIdentifier(SRC_MAIN_WEBAPP, "WEB-INF/web.xml"))) { webMvcOperations.installAllWebMvcArtifacts(); } final String topPackageName = projectOperations.getTopLevelPackage( projectOperations.getFocusedModuleName()) .getFullyQualifiedPackageName(); final Set<FileDetails> gwtConfigs = fileManager .findMatchingAntPath(projectOperations.getPathResolver() .getFocusedRoot(SRC_MAIN_JAVA) + File.separatorChar + topPackageName.replace('.', File.separatorChar) + File.separator + "*.gwt.xml"); final boolean gwtAlreadySetup = !gwtConfigs.isEmpty(); if (!gwtAlreadySetup) { String sourceAntPath = "setup/*"; final String targetDirectory = projectOperations.getPathResolver() .getFocusedIdentifier(SRC_MAIN_JAVA, topPackageName.replace('.', File.separatorChar)); updateFile(sourceAntPath, targetDirectory, "", false); sourceAntPath = "setup/client/*"; updateFile(sourceAntPath, targetDirectory + "/client", "", false); } for (final ClassOrInterfaceTypeDetails proxyOrRequest : typeLocationService .findClassesOrInterfaceDetailsWithAnnotation(ROO_GWT_MIRRORED_FROM)) { final ClassOrInterfaceTypeDetailsBuilder cidBuilder = new ClassOrInterfaceTypeDetailsBuilder( proxyOrRequest); if (proxyOrRequest.extendsType(ENTITY_PROXY) || proxyOrRequest.extendsType(OLD_ENTITY_PROXY)) { final AnnotationMetadata annotationMetadata = MemberFindingUtils .getAnnotationOfType(proxyOrRequest.getAnnotations(), ROO_GWT_MIRRORED_FROM); if (annotationMetadata != null) { final AnnotationMetadataBuilder annotationMetadataBuilder = new AnnotationMetadataBuilder( annotationMetadata); annotationMetadataBuilder.setAnnotationType(ROO_GWT_PROXY); cidBuilder.removeAnnotation(ROO_GWT_MIRRORED_FROM); cidBuilder.addAnnotation(annotationMetadataBuilder); typeManagementService.createOrUpdateTypeOnDisk(cidBuilder .build()); } } else if (proxyOrRequest.extendsType(REQUEST_CONTEXT) || proxyOrRequest.extendsType(OLD_REQUEST_CONTEXT)) { final AnnotationMetadata annotationMetadata = MemberFindingUtils .getAnnotationOfType(proxyOrRequest.getAnnotations(), ROO_GWT_MIRRORED_FROM); if (annotationMetadata != null) { final AnnotationMetadataBuilder annotationMetadataBuilder = new AnnotationMetadataBuilder( annotationMetadata); annotationMetadataBuilder .setAnnotationType(ROO_GWT_REQUEST); cidBuilder.removeAnnotation(ROO_GWT_MIRRORED_FROM); cidBuilder.addAnnotation(annotationMetadataBuilder); typeManagementService.createOrUpdateTypeOnDisk(cidBuilder .build()); } } } // Add GWT natures and builder names to maven eclipse plugin updateEclipsePlugin(); // Add outputDirectory to build element of pom updateBuildOutputDirectory(); final Element configuration = XmlUtils.getConfiguration(getClass()); // Add POM repositories updateRepositories(configuration); // Add dependencies updateDependencies(configuration); // Update web.xml updateWebXml(); // Update gwt-maven-plugin and others updateBuildPlugins(projectOperations .isFeatureInstalledInFocusedModule(FeatureNames.GAE)); } /** * Sets the POM's output directory to {@value #OUTPUT_DIRECTORY}, if it's * not already set to something else. */ private void updateBuildOutputDirectory() { // Read the POM final String pom = getPomPath(); final Document document = XmlUtils.readXml(fileManager .getInputStream(pom)); final Element root = document.getDocumentElement(); Element outputDirectoryElement = XmlUtils.findFirstElement( "/project/build/outputDirectory", root); if (outputDirectoryElement == null) { // Create it final Element buildElement = XmlUtils.findRequiredElement( "/project/build", root); outputDirectoryElement = DomUtils.createChildElement( "outputDirectory", buildElement, document); } outputDirectoryElement.setTextContent(OUTPUT_DIRECTORY); fileManager.createOrUpdateTextFileIfRequired(pom, XmlUtils.nodeToString(document), false); } private void updateBuildPlugins(final boolean isGaeEnabled) { // Update the POM final List<Plugin> plugins = new ArrayList<Plugin>(); final String xPathExpression = "/configuration/" + (isGaeEnabled ? "gae" : "gwt") + "/plugins/plugin"; final List<Element> pluginElements = XmlUtils.findElements( xPathExpression, XmlUtils.getConfiguration(getClass())); for (final Element pluginElement : pluginElements) { plugins.add(new Plugin(pluginElement)); } projectOperations.addBuildPlugins( projectOperations.getFocusedModuleName(), plugins); } private void updateDependencies(final Element configuration) { final List<Dependency> dependencies = new ArrayList<Dependency>(); final List<Element> gwtDependencies = XmlUtils.findElements( "/configuration/gwt/dependencies/dependency", configuration); for (final Element dependencyElement : gwtDependencies) { dependencies.add(new Dependency(dependencyElement)); } projectOperations.addDependencies( projectOperations.getFocusedModuleName(), dependencies); } /** * Updates the Eclipse plugin in the POM with the necessary GWT details */ private void updateEclipsePlugin() { // Load the POM final String pom = getPomPath(); final Document document = XmlUtils.readXml(fileManager .getInputStream(pom)); final Element root = document.getDocumentElement(); // Add the GWT "buildCommand" final Element additionalBuildCommandsElement = XmlUtils .findFirstElement(MAVEN_ECLIPSE_PLUGIN + "/configuration/additionalBuildcommands", root); Validate.notNull(additionalBuildCommandsElement, "additionalBuildcommands element of the maven-eclipse-plugin required"); Element gwtBuildCommandElement = XmlUtils.findFirstElement( "buildCommand[name = '" + GWT_BUILD_COMMAND + "']", additionalBuildCommandsElement); if (gwtBuildCommandElement == null) { gwtBuildCommandElement = DomUtils.createChildElement( "buildCommand", additionalBuildCommandsElement, document); final Element nameElement = DomUtils.createChildElement("name", gwtBuildCommandElement, document); nameElement.setTextContent(GWT_BUILD_COMMAND); } // Add the GWT "projectnature" final Element additionalProjectNaturesElement = XmlUtils .findFirstElement(MAVEN_ECLIPSE_PLUGIN + "/configuration/additionalProjectnatures", root); Validate.notNull(additionalProjectNaturesElement, "additionalProjectnatures element of the maven-eclipse-plugin required"); Element gwtProjectNatureElement = null; List<Element> gwtProjectNatureElements = XmlUtils.findElements("projectnature", additionalProjectNaturesElement); for (Element element: gwtProjectNatureElements) { if (GWT_PROJECT_NATURE.equals(element.getTextContent())) { gwtProjectNatureElement = element; break; } } if (gwtProjectNatureElement == null) { gwtProjectNatureElement = new XmlElementBuilder("projectnature", document).setText(GWT_PROJECT_NATURE).build(); additionalProjectNaturesElement .appendChild(gwtProjectNatureElement); } fileManager.createOrUpdateTextFileIfRequired(pom, XmlUtils.nodeToString(document), false); } private void updateFile(final String sourceAntPath, String targetDirectory, final String segmentPackage, final boolean overwrite) { if (!targetDirectory.endsWith(File.separator)) { targetDirectory += File.separator; } if (!fileManager.exists(targetDirectory)) { fileManager.createDirectory(targetDirectory); } final String path = FileUtils.getPath(getClass(), sourceAntPath); final Iterable<URL> urls = OSGiUtils.findEntriesByPattern( context.getBundleContext(), path); Validate.notNull(urls, "Could not search bundles for resources for Ant Path '" + path + "'"); for (final URL url : urls) { String fileName = url.getPath().substring( url.getPath().lastIndexOf('/') + 1); fileName = fileName.replace("-template", ""); final String targetFilename = targetDirectory + fileName; InputStream inputStream = null; OutputStream outputStream = null; try { if (fileManager.exists(targetFilename) && !overwrite) { continue; } if (targetFilename.endsWith("png")) { inputStream = url.openStream(); outputStream = fileManager.createFile(targetFilename) .getOutputStream(); IOUtils.copy(inputStream, outputStream); } else { // Read template and insert the user's package String input = IOUtils.toString(url); input = processTemplate(input, segmentPackage); // Output the file for the user fileManager.createOrUpdateTextFileIfRequired( targetFilename, input, true); } } catch (final IOException e) { throw new IllegalStateException("Unable to create '" + targetFilename + "'", e); } finally { IOUtils.closeQuietly(inputStream); IOUtils.closeQuietly(outputStream); } } } public void updateGaeConfiguration() { final boolean isGaeEnabled = projectOperations .isFeatureInstalledInFocusedModule(FeatureNames.GAE); final boolean hasGaeStateChanged = wasGaeEnabled == null || isGaeEnabled != wasGaeEnabled; if (!isInstalledInModule(projectOperations.getFocusedModuleName()) || !hasGaeStateChanged) { return; } wasGaeEnabled = isGaeEnabled; // Update the GaeHelper type updateGaeHelper(); gwtTypeService.buildType(GwtType.APP_REQUEST_FACTORY, gwtTemplateService.getStaticTemplateTypeDetails( GwtType.APP_REQUEST_FACTORY, projectOperations .getFocusedProjectMetadata().getModuleName()), projectOperations.getFocusedModuleName()); // Ensure the gwt-maven-plugin appropriate to a GAE enabled or disabled // environment is updated updateBuildPlugins(isGaeEnabled); // If there is a class that could possibly import from the appengine // sdk, denoted here as having Gae in the type name, // then we need to add the appengine-api-1.0-sdk dependency to the // pom.xml file final String rootPath = projectOperations.getPathResolver() .getFocusedRoot(ROOT); final Set<FileDetails> files = fileManager.findMatchingAntPath(rootPath + "/**/*Gae*.java"); if (!files.isEmpty()) { final Element configuration = XmlUtils.getConfiguration(getClass()); final Element gaeDependency = XmlUtils .findFirstElement( "/configuration/gae/dependencies/dependency", configuration); projectOperations.addDependency(projectOperations .getFocusedModuleName(), new Dependency(gaeDependency)); } // Copy across any missing files, only if GAE state has changed and is // now enabled if (isGaeEnabled) { copyDirectoryContents(); } } private void updateGaeHelper() { final String sourceAntPath = "module/client/scaffold/gae/GaeHelper-template.java"; final String segmentPackage = "client.scaffold.gae"; final String targetDirectory = projectOperations.getPathResolver() .getFocusedIdentifier( SRC_MAIN_JAVA, projectOperations .getTopLevelPackage( projectOperations .getFocusedModuleName()) .getFullyQualifiedPackageName() .replace('.', File.separatorChar) + File.separator + "client" + File.separator + "scaffold" + File.separator + "gae"); updateFile(sourceAntPath, targetDirectory, segmentPackage, true); } private void updateRepositories(final Element configuration) { final List<Repository> repositories = new ArrayList<Repository>(); final List<Element> gwtRepositories = XmlUtils.findElements( "/configuration/gwt/repositories/repository", configuration); for (final Element repositoryElement : gwtRepositories) { repositories.add(new Repository(repositoryElement)); } projectOperations.addRepositories( projectOperations.getFocusedModuleName(), repositories); repositories.clear(); final List<Element> gwtPluginRepositories = XmlUtils.findElements( "/configuration/gwt/pluginRepositories/pluginRepository", configuration); for (final Element repositoryElement : gwtPluginRepositories) { repositories.add(new Repository(repositoryElement)); } projectOperations.addPluginRepositories( projectOperations.getFocusedModuleName(), repositories); } private void updateScaffoldBoilerPlate() { final String targetDirectory = projectOperations.getPathResolver() .getFocusedIdentifier( SRC_MAIN_JAVA, projectOperations .getTopLevelPackage( projectOperations .getFocusedModuleName()) .getFullyQualifiedPackageName() .replace('.', File.separatorChar)); deleteUntouchedSetupFiles("setup/*", targetDirectory); deleteUntouchedSetupFiles("setup/client/*", targetDirectory + "/client"); copyDirectoryContents(); updateGaeHelper(); } private void updateWebXml() { final String webXmlpath = projectOperations.getPathResolver() .getFocusedIdentifier(SRC_MAIN_WEBAPP, "WEB-INF/web.xml"); final Document webXml = XmlUtils.readXml(fileManager .getInputStream(webXmlpath)); final Element root = webXml.getDocumentElement(); WebXmlUtils.addServlet( "requestFactory", projectOperations.getTopLevelPackage(projectOperations .getFocusedModuleName()) + ".server.CustomRequestFactoryServlet", "/gwtRequest", null, webXml, null); if (projectOperations .isFeatureInstalledInFocusedModule(FeatureNames.GAE)) { WebXmlUtils .addFilter( "GaeAuthFilter", GwtPath.SERVER_GAE.packageName(projectOperations .getTopLevelPackage(projectOperations .getFocusedModuleName())) + ".GaeAuthFilter", "/gwtRequest/*", webXml, "This filter makes GAE authentication services visible to a RequestFactory client."); final String displayName = "Redirect to the login page if needed before showing any html pages"; final WebXmlUtils.WebResourceCollection webResourceCollection = new WebXmlUtils.WebResourceCollection( "Login required", null, Collections.singletonList("*.html"), new ArrayList<String>()); final ArrayList<String> roleNames = new ArrayList<String>(); roleNames.add("*"); final String userDataConstraint = null; WebXmlUtils.addSecurityConstraint(displayName, Collections.singletonList(webResourceCollection), roleNames, userDataConstraint, webXml, null); } else { final Element filter = XmlUtils.findFirstElement( "/web-app/filter[filter-name = 'GaeAuthFilter']", root); if (filter != null) { filter.getParentNode().removeChild(filter); } final Element filterMapping = XmlUtils.findFirstElement( "/web-app/filter-mapping[filter-name = 'GaeAuthFilter']", root); if (filterMapping != null) { filterMapping.getParentNode().removeChild(filterMapping); } final Element securityConstraint = XmlUtils.findFirstElement( "security-constraint", root); if (securityConstraint != null) { securityConstraint.getParentNode().removeChild( securityConstraint); } } removeIfFound("/web-app/error-page", root); fileManager.createOrUpdateTextFileIfRequired(webXmlpath, XmlUtils.nodeToString(webXml), false); } }