package org.springframework.roo.project;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.apache.commons.io.FilenameUtils;
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.Service;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import org.springframework.roo.file.monitor.FileMonitorService;
import org.springframework.roo.metadata.MetadataDependencyRegistry;
import org.springframework.roo.metadata.MetadataService;
import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.project.maven.Pom;
import org.springframework.roo.project.maven.PomFactory;
import org.springframework.roo.shell.Shell;
import org.springframework.roo.support.logging.HandlerUtils;
import org.springframework.roo.support.osgi.OSGiUtils;
import org.springframework.roo.support.util.FileUtils;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@Component
@Service
public class PomManagementServiceImpl implements PomManagementService {
private BundleContext context;
private static final Logger LOGGER = HandlerUtils.getLogger(PomManagementServiceImpl.class);
protected void activate(final ComponentContext cContext) {
context = cContext.getBundleContext();
final File projectDirectory =
new File(StringUtils.defaultIfEmpty(OSGiUtils.getRooWorkingDirectory(cContext),
FileUtils.CURRENT_DIRECTORY));
projectRootDirectory = FileUtils.getCanonicalPath(projectDirectory);
}
private static class PomComparator implements Comparator<String> {
private final Map<String, Pom> pomMap;
/**
* Constructor
*
* @param pomMap
*/
private PomComparator(final Map<String, Pom> pomMap) {
this.pomMap = pomMap;
}
public int compare(final String s1, final String s2) {
final String p1 = pomMap.get(s1).getRoot() + SEPARATOR;
final String p2 = pomMap.get(s2).getRoot() + SEPARATOR;
if (p1.startsWith(p2)) {
return -1;
} else if (p2.startsWith(p1)) {
return 1;
}
return 0;
}
}
private static final String SEPARATOR = File.separator;
private static final String DEFAULT_POM_NAME = "pom.xml";
private static final String DEFAULT_RELATIVE_PATH = ".." + SEPARATOR + DEFAULT_POM_NAME;
FileManager fileManager;
FileMonitorService fileMonitorService;
MetadataDependencyRegistry metadataDependencyRegistry;
MetadataService metadataService;
PomFactory pomFactory;
Shell shell;
private String focusedModulePath;
private final Map<String, Pom> pomMap = new LinkedHashMap<String, Pom>();
private String projectRootDirectory;
private final Set<String> toBeParsed = new HashSet<String>();
/**
* For test cases to set up the state of this service
*
* @param pom the POM to add (required)
*/
void addPom(final Pom pom) {
pomMap.put(pom.getPath(), pom);
}
private void findUnparsedPoms() {
for (final String change : getFileMonitorService().getDirtyFiles(getClass().getName())) {
if (change.endsWith(DEFAULT_POM_NAME)) {
toBeParsed.add(change);
}
}
}
public Pom getFocusedModule() {
updatePomCache();
if (focusedModulePath == null && getRootPom() != null) {
focusedModulePath = getRootPom().getPath();
}
return getPomFromPath(focusedModulePath);
}
public String getFocusedModuleName() {
if (getFocusedModule() == null) {
return "";
}
return getFocusedModule().getModuleName();
}
public Pom getModuleForFileIdentifier(final String fileIdentifier) {
updatePomCache();
String startingPoint = FileUtils.getFirstDirectory(fileIdentifier);
String pomPath = FileUtils.ensureTrailingSeparator(startingPoint) + DEFAULT_POM_NAME;
File pom = new File(pomPath);
while (!pom.exists()) {
if (startingPoint.equals(SEPARATOR)) {
break;
}
startingPoint = StringUtils.removeEnd(startingPoint, SEPARATOR);
if (startingPoint.lastIndexOf(SEPARATOR) < 0) {
break;
}
startingPoint = startingPoint.substring(0, startingPoint.lastIndexOf(SEPARATOR));
startingPoint = StringUtils.removeEnd(startingPoint, SEPARATOR);
pomPath = FileUtils.ensureTrailingSeparator(startingPoint) + DEFAULT_POM_NAME;
pom = new File(pomPath);
}
return getPomFromPath(pomPath);
}
private String getModuleName(final String pomDirectory) {
final String normalisedRootPath = FileUtils.ensureTrailingSeparator(projectRootDirectory);
final String normalisedPomDirectory = FileUtils.ensureTrailingSeparator(pomDirectory);
final String moduleDirectory =
StringUtils.removeStart(normalisedPomDirectory, normalisedRootPath);
return FilenameUtils.getBaseName(StringUtils.stripEnd(moduleDirectory, SEPARATOR));
}
public Collection<String> getModuleNames() {
final Set<String> moduleNames = new HashSet<String>();
for (final Pom module : pomMap.values()) {
moduleNames.add(module.getModuleName());
}
return moduleNames;
}
public Pom getPomFromModuleName(final String moduleName) {
for (final Pom pom : getPoms()) {
if (pom.getModuleName().equals(moduleName)) {
return pom;
}
}
return null;
}
public Pom getPomFromPath(final String pomPath) {
updatePomCache();
return pomMap.get(pomPath);
}
public Collection<Pom> getPoms() {
updatePomCache();
return new ArrayList<Pom>(pomMap.values());
}
public Pom getRootPom() {
updatePomCache();
return pomMap.get(projectRootDirectory + SEPARATOR + DEFAULT_POM_NAME);
}
private Set<Pom> parseUnparsedPoms() {
final Map<String, String> pomModuleMap = new HashMap<String, String>();
final Set<Pom> newPoms = new HashSet<Pom>();
for (final Iterator<String> iter = toBeParsed.iterator(); iter.hasNext();) {
final String pathToChangedPom = iter.next();
if (new File(pathToChangedPom).exists()) {
String pomContents = "";
try {
pomContents =
org.apache.commons.io.FileUtils.readFileToString(new File(pathToChangedPom));
} catch (IOException ignored) {
}
if (StringUtils.isNotBlank(pomContents)) {
final Element rootElement = XmlUtils.stringToElement(pomContents);
resolvePoms(rootElement, pathToChangedPom, pomModuleMap);
final String moduleName = getModuleName(FileUtils.getFirstDirectory(pathToChangedPom));
final Pom pom = getPomFactory().getInstance(rootElement, pathToChangedPom, moduleName);
Validate.notNull(pom, "POM is null for module '%s' and path '%s'", moduleName,
pathToChangedPom);
pomMap.put(pathToChangedPom, pom);
newPoms.add(pom);
iter.remove();
}
}
}
return newPoms;
}
private void resolveChildModulePoms(final Element pomRoot, final String pomPath,
final Map<String, String> pomSet) {
for (final Element module : XmlUtils.findElements("/project/modules/module", pomRoot)) {
final String moduleName = module.getTextContent();
if (StringUtils.isNotBlank(moduleName)) {
final String modulePath = resolveRelativePath(pomPath, moduleName);
final boolean alreadyDiscovered = pomSet.containsKey(modulePath);
pomSet.put(modulePath, moduleName);
if (!alreadyDiscovered) {
final Document pomDocument =
XmlUtils.readXml(getFileManager().getInputStream(modulePath));
final Element root = pomDocument.getDocumentElement();
resolvePoms(root, modulePath, pomSet);
}
}
}
}
private void resolveParentPom(final String pomPath, final Map<String, String> pomSet,
final Element parentElement) {
final String relativePath =
XmlUtils.getTextContent("/relativePath", parentElement, DEFAULT_RELATIVE_PATH);
final String parentPomPath = resolveRelativePath(pomPath, relativePath);
final boolean alreadyDiscovered = pomSet.containsKey(parentPomPath);
if (!alreadyDiscovered) {
pomSet.put(parentPomPath, pomSet.get(parentPomPath));
if (new File(parentPomPath).isFile()) {
final Document pomDocument =
XmlUtils.readXml(getFileManager().getInputStream(parentPomPath));
final Element root = pomDocument.getDocumentElement();
resolvePoms(root, parentPomPath, pomSet);
}
}
}
private void resolvePoms(final Element pomRoot, final String pomPath,
final Map<String, String> pomSet) {
pomSet.put(pomPath, pomSet.get(pomPath)); // ensures this key exists
final Element parentElement = XmlUtils.findFirstElement("/project/parent", pomRoot);
if (parentElement != null) {
resolveParentPom(pomPath, pomSet, parentElement);
}
resolveChildModulePoms(pomRoot, pomPath, pomSet);
}
private String resolveRelativePath(String relativeTo, final String relativePath) {
if (relativeTo.endsWith(SEPARATOR)) {
relativeTo = relativeTo.substring(0, relativeTo.length() - 1);
}
while (new File(relativeTo).isFile()) {
relativeTo = relativeTo.substring(0, relativeTo.lastIndexOf(SEPARATOR));
}
final String[] relativePathSegments = relativePath.split(FileUtils.getFileSeparatorAsRegex());
int backCount = 0;
for (final String relativePathSegment : relativePathSegments) {
if (relativePathSegment.equals("..")) {
backCount++;
} else {
break;
}
}
final StringBuilder sb = new StringBuilder();
for (int i = backCount; i < relativePathSegments.length; i++) {
sb.append(relativePathSegments[i]);
sb.append(SEPARATOR);
}
while (backCount > 0) {
relativeTo = relativeTo.substring(0, relativeTo.lastIndexOf(SEPARATOR));
backCount--;
}
String path = relativeTo + SEPARATOR + sb.toString();
if (new File(path).isDirectory()) {
path = path + DEFAULT_POM_NAME;
}
if (path.endsWith(SEPARATOR)) {
path = path.substring(0, path.length() - 1);
}
return path;
}
public void setFocusedModule(final Pom focusedModule) {
Validate.notNull(focusedModule, "Module required");
if (focusedModule.getPath().equals(focusedModulePath)) {
return;
}
focusedModulePath = focusedModule.getPath();
getShell().setPromptPath(focusedModule.getModuleName());
}
private void sortPomMap() {
final List<String> sortedPomPaths = new ArrayList<String>(pomMap.keySet());
Collections.sort(sortedPomPaths, new PomComparator(pomMap));
final Map<String, Pom> sortedPomMap = new LinkedHashMap<String, Pom>();
for (final String pomPath : sortedPomPaths) {
sortedPomMap.put(pomPath, pomMap.get(pomPath));
}
pomMap.clear();
pomMap.putAll(sortedPomMap);
}
private void updatePomCache() {
findUnparsedPoms();
final Collection<Pom> newPoms = parseUnparsedPoms();
if (!newPoms.isEmpty()) {
sortPomMap();
}
updateProjectMetadataForModules(newPoms);
}
private void updateProjectMetadataForModules(final Iterable<Pom> newPoms) {
for (final Pom pom : newPoms) {
final String projectMetadataId = ProjectMetadata.getProjectIdentifier(pom.getModuleName());
getMetadataService().evictAndGet(projectMetadataId);
getMetadataDependencyRegistry().notifyDownstream(projectMetadataId);
}
}
/**
* Method to get FileMonitorService Service implementation
*
* @return
*/
public FileMonitorService getFileMonitorService() {
if (fileMonitorService == null) {
// Get all Services implement FileMonitorService interface
try {
ServiceReference<?>[] references =
context.getAllServiceReferences(FileMonitorService.class.getName(), null);
for (ServiceReference<?> ref : references) {
fileMonitorService = (FileMonitorService) context.getService(ref);
return fileMonitorService;
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load FileMonitorService on PomManagementServiceImpl.");
return null;
}
} else {
return fileMonitorService;
}
}
/**
* Method to get PomFactory Service implementation
*
* @return
*/
public PomFactory getPomFactory() {
if (pomFactory == null) {
// Get all Services implement PomFactory interface
try {
ServiceReference<?>[] references =
context.getAllServiceReferences(PomFactory.class.getName(), null);
for (ServiceReference<?> ref : references) {
pomFactory = (PomFactory) context.getService(ref);
return pomFactory;
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load PomFactory on PomManagementServiceImpl.");
return null;
}
} else {
return pomFactory;
}
}
/**
* Method to get FileManager Service implementation
*
* @return
*/
public FileManager getFileManager() {
if (fileManager == null) {
// Get all Services implement FileManager interface
try {
ServiceReference<?>[] references =
context.getAllServiceReferences(FileManager.class.getName(), null);
for (ServiceReference<?> ref : references) {
fileManager = (FileManager) context.getService(ref);
return fileManager;
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load FileManager on PomManagementServiceImpl.");
return null;
}
} else {
return fileManager;
}
}
/**
* Method to get MetadataService Service implementation
*
* @return
*/
public MetadataService getMetadataService() {
if (metadataService == null) {
// Get all Services implement MetadataService interface
try {
ServiceReference<?>[] references =
context.getAllServiceReferences(MetadataService.class.getName(), null);
for (ServiceReference<?> ref : references) {
metadataService = (MetadataService) context.getService(ref);
return metadataService;
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load MetadataService on PomManagementServiceImpl.");
return null;
}
} else {
return metadataService;
}
}
/**
* Method to get MetadataDependencyRegistry Service implementation
*
* @return
*/
public MetadataDependencyRegistry getMetadataDependencyRegistry() {
if (metadataDependencyRegistry == null) {
// Get all Services implement MetadataDependencyRegistry interface
try {
ServiceReference<?>[] references =
context.getAllServiceReferences(MetadataDependencyRegistry.class.getName(), null);
for (ServiceReference<?> ref : references) {
metadataDependencyRegistry = (MetadataDependencyRegistry) context.getService(ref);
return metadataDependencyRegistry;
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load MetadataDependencyRegistry on PomManagementServiceImpl.");
return null;
}
} else {
return metadataDependencyRegistry;
}
}
/**
* Method to get Shell Service implementation
*
* @return
*/
public Shell getShell() {
if (shell == null) {
// Get all Services implement Shell interface
try {
ServiceReference<?>[] references =
context.getAllServiceReferences(Shell.class.getName(), null);
for (ServiceReference<?> ref : references) {
shell = (Shell) context.getService(ref);
return shell;
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load Shell on PomManagementServiceImpl.");
return null;
}
} else {
return shell;
}
}
}