Skip to content

Commit

Permalink
Merge pull request #39 from concon121/feature/dependencyMediation
Browse files Browse the repository at this point in the history
Feature/dependency mediation
  • Loading branch information
concon121 committed May 31, 2016
2 parents b3e09b9 + cfe645c commit b3100bd
Show file tree
Hide file tree
Showing 10 changed files with 433 additions and 308 deletions.
31 changes: 20 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@ Generates a .classpath file based on your maven pom file.
- Java classpath is configured via a module specific .classpath file.
- Capable of detecting when your pom files change and updating your classpath accordingly.
- On screen warning messages when you enter maven dependencies which do not exist or can not be resolved in your local repo.
- Recursively searches <parent> to resolve version numbers from your <dependencyManagement>.
- Recursively searches <parent> to resolve property placeholders from user defined <properties>.
- Dependencies defined in parents of your pom files are identified and added to the classpath.
- Capable of resolving dependency information from dependency management structures within the parental hierarchy.
- Capable of resolving property placeholders from properties defined within the parental hierarchy, and getting the value of properties defined as environment variables.
- Dependencies defined within the parental hierarchy of your pom files are identified and added to the classpath.
- Transitive dependencies are identified and added to the classpath.
- Implemented [dependency mediation](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Transitive_Dependencies) to ensure that the dependency versions you specify in your pom files take precedence over dependency versions being inherited transitively.

![atom-maven](https://cloud.githubusercontent.com/assets/12021575/15276879/4429112e-1aec-11e6-8bbe-c24901b3ee17.JPG)
![310516](https://cloud.githubusercontent.com/assets/12021575/15692408/12018824-2786-11e6-8cac-289fd0af4076.JPG)

## In Progress
- Implement [dependency mediation](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Transitive_Dependencies) for transitive dependencies.
- Bug Fix: Resolve Maven installation directory from M2, M2_HOME, etc environment variables if it can not be found on the PATH.
- Bug Fix: Stop atom-maven if Maven is not installed.


## To Do
- If a dependency is identified as not existing in the local repository, then use maven to build the project so an attempt is made to download the dependency from the remote repository before showing the user that there is an error.
Expand All @@ -30,33 +33,39 @@ Generates a .classpath file based on your maven pom file.
- Support for import scoped dependencies.
- Support for dependency exclusions.
- Support for optional dependencies.
- Support for properties defined in the users settings.xml file.

## Known Issues
- Dependency mediation hasn't yet been implemented, so you may find you have the wrong versions of dependencies on your classpath.
- "Unclosed root tag" error message in the console. An intermittent problem which is being caused because another package I'm using to format my files on save is modifying the file as atom-maven is trying to read from it.
- "Unclosed root tag" error message in the console. An intermittent problem which is being caused because another package I'm using to format my files on save is modifying the file as atom-maven is trying to read from it. This seems to only happen when I add comments to my pom files.
- The message panel is a little bit jumpy while atom-maven is loading the classpath.
- Maven installation is not found if it has been put on the PATH under another environment variable e.g. export PATH = $PATH:$M2_HOME.
- If Maven installation is not found, atom-maven still tries to load the poms and resolve the classpath. This is an issue as it will likely attempt to read files which dont exist.

## Backlog and Issues
The complete list of features which needs to be implemented, future enhancements, known issues and bugs can be found on the GitHub repository [here](https://github.com/concon121/atom-maven/issues).

## Contributing
Contributions are always welcome, there is still a lot of work to be done! Feel free to pick up an issue in the backlog and open a pull request to get the conversation going. I am more than happy to provide help and direction, and very welcoming of advice and suggestions.

## To be used with
## What can I use atom-maven for?

I started developing atom-maven as I wanted to be able to write my Java code in Atom. There are a couple of packages I found which make this easier, but they depend on a .classpath file being configured. As a user of Maven, Maven configures your classpath and makes sure all your dependencies are available when you need them to be. The atom-maven package replicates this functionality and generates the .classpath file other Atom Java packages need, based on your Maven pom files!

There are a few plugins out there which can use the .classpath file atom-maven generates.
### Useful Java Packages

* [autocomplete-java](https://atom.io/packages/autocomplete-java)
* [linter-javac](https://atom.io/packages/linter-javac)

### autocomplete-java
#### autocomplete-java
Capable of reading the generated .classpath file, this package provides functionlity to organise imports, complete packages and classes, examine methods etc...

Note. git ignored .classpath files are not discovered.

### linter-javac
#### linter-javac
Capable of reading the generated .classpath file, this package will attempt to compile your .java files and show you all the compile time problems with your code.

## Kudos
Kudos to the following, for making my life easier!

* [atom-message-panel](https://github.com/tcarlsen/atom-message-panel)
* [xml2js](https://github.com/Leonidas-from-XIV/node-xml2js)
49 changes: 21 additions & 28 deletions lib/atom-maven.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,31 @@ export default {
},

setup: function () {
console.info('atom-maven is configuring your classpath');
ui.info('atom-maven is configuring your classpath');
var self = this;
mvn.getPoms((pom) => {
var cp = self.getClasspath(pom.pomPath);
var writeCp = self.writeClasspath(cp, pom, self);
pom.registerLoadCompleteEvent(() => {
self.writeClasspath(cp, pom, self);
});
pom.registerChangeEvent(() => {
self.handleChange(pom, writeCp);
ui.clearFileMessages(pom.pomPath);
ui.clearSuccess();
ui.clearInfo();
pom.reload();
});
self.handleChange(pom, writeCp);
});
},

handleChange: function (pom, writeCp) {
if (!pom.changing) {
try {
pom.reload(writeCp);
} catch (err) {
console.error(err);
pom.changing = false;
}
}
},

writeClasspath: function (cp, pom, self) {
return function () {
setTimeout(() => {
ui.clearFileMessages(pom.pomPath);
var locations = self.initLocations() + self.getClasspathFromDependencies(pom, self);
fs.writeFile(cp, locations, (err) => {
if (err) console.error(err);
pom.changing = false;
});
console.info('atom-maven has finished configuring the classpath: ' + cp);
}, 1e4);
};

var locations = self.initLocations() + self.getClasspathFromDependencies(pom, self);
fs.writeFile(cp, locations, (err) => {
if (err) ui.error(err);
pom.changing = false;
});
ui.success('atom-maven has finished configuring the classpath: ' + cp);

},

getClasspath: function (pomPath) {
Expand All @@ -61,7 +51,8 @@ export default {

getClasspathFromDependencies: function (pom, self) {
var locations = '';
for (var dependency of pom.getDependenciesInClasspath()) {

for (var dependency of pom.classpath) {
if (dependency.existsInRepo) {
locations = locations + dependency.repoLocation + ';';
} else if (pom.xml) {
Expand All @@ -78,8 +69,10 @@ export default {
var substr = xml.substr(0, result.index);
var lineNo = substr.match(/\n/g).length + 1;
var message = mvn.getDependencyNotFoundMessage(dependency);
var preview = '<dependency>\n\t<groupId>' + dependency.groupId + '</groupId>\n\t<artifactId>' + dependency.artifactId + '</artifactId>\n\t<version>' + dependency.version + '</version>\n</dependency>';


ui.addLineMessage(message, lineNo, 0, file, 'error');
ui.error(message, lineNo, 0, file, preview);

}

Expand Down
3 changes: 1 addition & 2 deletions lib/file-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ var FileUtils = function () {
fs.accessSync(file, fs.F_OK);
} catch (err) {
if (doReport) {
console.warn('File does not exist: ' + file);
ui.addPlainMessage('File does not exist: ' + file, 'warning');
ui.warning('File does not exist: ' + file);
}
exists = false;
}
Expand Down
8 changes: 5 additions & 3 deletions lib/maven-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var MavenUtils = function () {
var e = mavenElems[0];
settingsFileLocation = (e.endsWith('bin')) ? e.replace('bin', self.settings) : e.concat(self.settings);
} else {
ui.addPlainMessage('Maven has not been found on the PATH, please ensure that Maven has been installed.', 'warning');
ui.warning('Maven has not been found on the PATH, please ensure that Maven has been installed.');
}
return settingsFileLocation;
},
Expand Down Expand Up @@ -99,11 +99,12 @@ var MavenUtils = function () {
const self = this;

self.setMavenRepo(() => {
atom.workspace.scan(/\<project.*maven/g, (match) => {
atom.workspace.scan(/\<project.*/g, (match) => {
if (match.filePath.endsWith('pom.xml') || match.filePath.endsWith('.pom')) {
var promise = factory.getInstance(match.filePath, callback);
promise.then((pom) => {
self.workspace.add(pom);
callback(pom);
});
}
});
Expand All @@ -123,7 +124,7 @@ var MavenUtils = function () {
result = $($.parseXML(xml)).find(selector);
} catch (err) {
console.error(err);
ui.addLineMessage('Invalid XML Document', null, null, (pom) ? pom.pomPath : null, 'error');
ui.error('Invalid XML Document', null, null, (pom) ? pom.pomPath : null);
}
return (result && children) ? result.children() : result;
},
Expand All @@ -148,6 +149,7 @@ var MavenUtils = function () {
getGAVT: function (pom, elem, type) {
var version,
scope;

if (pom.hasContent(elem.version)) version = elem.version[0];
if (pom.hasContent(elem.scope)) scope = elem.scope[0];
return {
Expand Down
67 changes: 21 additions & 46 deletions lib/pom-factory.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
const Pom = require('./pom');
const $ = require('jquery');
const _ = require('underscore');
const fs = require('fs');
const xml2js = require('xml2js').Parser();
const file = require('./file-utils');

class PomFactory {
Expand All @@ -29,54 +25,33 @@ PomFactory.prototype.getInstance = function (path, opts, callback) {
}

var promise = new Promise((resolve, reject) => {
var found = self.registry.find(path, opts);
var projectPath;
if (found) {
if (found.pomPath && found.pomPath !== path) {
for (projectPath of atom.project.getPaths()) {
if (path.indexOf(projectPath) > 0) {
found.pomPath = path;
}
}
}
if (callback && typeof callback === 'function') {
callback();
}
resolve(found);
} else {
if (file.fileExists(path)) {
newpom = new Pom(opts);
if (!self.workspace.contains(newpom)) {
self.registry.put(path, newpom);
setTimeout(() => {
var found = self.registry.contains(path, opts);
if (!found) found = self.workspace.contains(opts);
if (found) {
if (callback && typeof callback === 'function') {
callback();
}
fs.readFile(path, 'utf8', (err, xml) => {
if (err) {
console.error('Failed to read file: ', path);
} else {
xml2js.parseString(xml, function (err, result) {
if (err) reject(err);
newpom.pomPath = path;
newpom.xml = xml;
$.extend(newpom, result);
if (_.isEmpty(newpom.groupId)) {
newpom.init(require('./maven-utils').getGAVT(newpom, newpom.project, 'pom'));
}
newpom.load(callback);
resolve(newpom);
});
}
});
resolve(found);
} else {
if (sourcepom && sourcepom.pomPath) {
for (projectPath of atom.project.getPaths()) {
if (sourcepom.pomPath.indexOf(projectPath) >= 0) {
newpom = new Pom(opts);
resolve(newpom);
if (file.fileExists(path)) {
newpom = new Pom(opts, path);
this.registry.put(path, newpom);
resolve(newpom);
} else {
// if we are loading a dependency of a workspace pom which doesnt exist.
if (sourcepom && sourcepom.pomPath) {
for (var projectPath of atom.project.getPaths()) {
if (sourcepom.pomPath.indexOf(projectPath) >= 0) {
newpom = new Pom(opts, path);
this.registry.put(path, newpom);
resolve(newpom);
}
}
}
}
}
}
}, 1e3);
});
return promise;
};
Expand Down
54 changes: 25 additions & 29 deletions lib/pom-registry.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,35 @@
var PomRegistry = function () {
return {
class PomRegistry {

registry: new Map(),
constructor() {
this.registry = new Map();
}

has: function (key) {
return this.registry.has(key);
},
}

put: function (key, value) {
if (!this.has(key)) this.registry.set(key, value);
else console.warn('Attempt to overwrite pom file was blocked', key, value);
},
PomRegistry.prototype.has = function (key) {
return this.registry.has(key);
};

get: function (key) {
return this.registry.get(key);
},
PomRegistry.prototype.put = function (key, value) {
if (!this.has(key) && (key.indexOf('undefined') < 0)) this.registry.set(key, value);
else console.warn('Attempt to overwrite pom file was blocked', key, value);
};

find: function (key, value) {
PomRegistry.prototype.get = function (key) {
return this.registry.get(key);
};

if (value) {
for (var v of this.registry.values()) {
if (v.equals(value)) {
return v;
}
}
} else {
return this.registry.get(key);
PomRegistry.prototype.contains = function (key, value) {
if (value) {
for (var v of this.registry.values()) {
if (v.equals(value)) {
return v;
}

return null;

}

};

} else {
return this.registry.get(key);
}
return null;
};

module.exports = PomRegistry();
module.exports = new PomRegistry();
Loading

0 comments on commit b3100bd

Please sign in to comment.