package org.springframework.roo.addon.solr; import static org.springframework.roo.model.RooJavaType.ROO_SOLR_SEARCHABLE; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Properties; import java.util.Set; import org.apache.commons.io.IOUtils; 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.classpath.TypeLocationService; import org.springframework.roo.classpath.TypeManagementService; import org.springframework.roo.classpath.customdata.CustomDataKeys; import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails; import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetailsBuilder; import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder; import org.springframework.roo.model.JavaType; import org.springframework.roo.process.manager.FileManager; import org.springframework.roo.process.manager.MutableFile; import org.springframework.roo.project.Dependency; import org.springframework.roo.project.FeatureNames; 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.XmlElementBuilder; import org.springframework.roo.support.util.XmlUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Provides Search configuration operations. * * @author Stefan Schmidt * @since 1.1 */ @Component @Service public class SolrOperationsImpl implements SolrOperations { @Reference private FileManager fileManager; @Reference private ProjectOperations projectOperations; @Reference private TypeLocationService typeLocationService; @Reference private TypeManagementService typeManagementService; public void addAll() { final Set<ClassOrInterfaceTypeDetails> cids = typeLocationService .findClassesOrInterfaceDetailsWithTag(CustomDataKeys.PERSISTENT_TYPE); for (final ClassOrInterfaceTypeDetails cid : cids) { if (!Modifier.isAbstract(cid.getModifier())) { addSolrSearchableAnnotation(cid); } } } public void addSearch(final JavaType javaType) { Validate.notNull(javaType, "Java type required"); final ClassOrInterfaceTypeDetails cid = typeLocationService .getTypeDetails(javaType); if (cid == null) { throw new IllegalArgumentException("Cannot locate source for '" + javaType.getFullyQualifiedTypeName() + "'"); } if (Modifier.isAbstract(cid.getModifier())) { throw new IllegalStateException( "The class specified is an abstract type. Can only add solr search for concrete types."); } addSolrSearchableAnnotation(cid); } private void addSolrSearchableAnnotation( final ClassOrInterfaceTypeDetails cid) { if (cid.getTypeAnnotation(ROO_SOLR_SEARCHABLE) == null) { final ClassOrInterfaceTypeDetailsBuilder cidBuilder = new ClassOrInterfaceTypeDetailsBuilder( cid); cidBuilder.addAnnotation(new AnnotationMetadataBuilder( ROO_SOLR_SEARCHABLE)); typeManagementService.createOrUpdateTypeOnDisk(cidBuilder.build()); } } public boolean isSearchAvailable() { return solrPropsInstalled(); } public boolean isSolrInstallationPossible() { return projectOperations.isFocusedProjectAvailable() && !solrPropsInstalled() && projectOperations.isFeatureInstalled(FeatureNames.JPA); } public void setupConfig(final String solrServerUrl) { updateConfiguration(projectOperations.getFocusedModuleName()); updateSolrProperties(solrServerUrl); final String contextPath = projectOperations.getPathResolver() .getFocusedIdentifier(Path.SPRING_CONFIG_ROOT, "applicationContext.xml"); final Document appCtx = XmlUtils.readXml(fileManager .getInputStream(contextPath)); final Element root = appCtx.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.0.xsd"); } root.appendChild(new XmlElementBuilder("task:annotation-driven", appCtx).addAttribute("executor", "asyncExecutor") .addAttribute("mode", "aspectj").build()); root.appendChild(new XmlElementBuilder("task:executor", appCtx) .addAttribute("id", "asyncExecutor") .addAttribute("pool-size", "${executor.poolSize}").build()); } final Element solrServer = XmlUtils.findFirstElement( "/beans/bean[@id='solrServer']", root); if (solrServer != null) { return; } root.appendChild(new XmlElementBuilder("bean", appCtx) .addAttribute("id", "solrServer") .addAttribute("class", "org.apache.solr.client.solrj.impl.CommonsHttpSolrServer") .addChild( new XmlElementBuilder("constructor-arg", appCtx) .addAttribute("value", "${solr.serverUrl}") .build()).build()); DomUtils.removeTextNodes(root); fileManager.createOrUpdateTextFileIfRequired(contextPath, XmlUtils.nodeToString(appCtx), false); } private boolean solrPropsInstalled() { return fileManager.exists(projectOperations.getPathResolver() .getFocusedIdentifier(Path.SPRING_CONFIG_ROOT, "solr.properties")); } private void updateSolrProperties(final String solrServerUrl) { final String solrPath = projectOperations.getPathResolver() .getFocusedIdentifier(Path.SPRING_CONFIG_ROOT, "solr.properties"); final boolean solrExists = fileManager.exists(solrPath); final Properties props = new Properties(); InputStream inputStream = null; try { if (fileManager.exists(solrPath)) { inputStream = fileManager.getInputStream(solrPath); props.load(inputStream); } } catch (final IOException ioe) { throw new IllegalStateException(ioe); } finally { IOUtils.closeQuietly(inputStream); } props.put("solr.serverUrl", solrServerUrl); props.put("executor.poolSize", "10"); OutputStream outputStream = null; try { final MutableFile mutableFile = solrExists ? fileManager .updateFile(solrPath) : fileManager.createFile(solrPath); outputStream = mutableFile.getOutputStream(); props.store(outputStream, "Updated at " + new Date()); } catch (final IOException e) { throw new IllegalStateException(e); } finally { IOUtils.closeQuietly(outputStream); } } private void updateConfiguration(final String moduleName) { final Element configuration = XmlUtils.getConfiguration(getClass()); final List<Dependency> dependencies = new ArrayList<Dependency>(); final List<Element> emailDependencies = XmlUtils.findElements( "/configuration/solr/dependencies/dependency", configuration); for (final Element dependencyElement : emailDependencies) { dependencies.add(new Dependency(dependencyElement)); } projectOperations.addDependencies(moduleName, dependencies); } }